[scala] 할당 된 값이 아닌 단위로 평가되는 Scala 할당의 동기는 무엇입니까?

할당 된 값이 아닌 단위로 평가되는 Scala 할당의 동기는 무엇입니까?

I / O 프로그래밍의 일반적인 패턴은 다음과 같은 작업을 수행하는 것입니다.

while ((bytesRead = in.read(buffer)) != -1) { ...

하지만 스칼라에서는 불가능합니다.

bytesRead = in.read(buffer)

..은 새로운 bytesRead 값이 아닌 Unit을 반환합니다.

기능적 언어에서 빼놓을 수있는 흥미로운 것 같습니다. 왜 그렇게되었는지 궁금합니다.



답변

나는 할당이 단위가 아닌 할당 된 값을 반환하도록 옹호했습니다. Martin과 나는 이에 대해 앞뒤로 갔지만 그의 주장은 시간의 95 %를 뽑아 내기 위해 스택에 값을 두는 것은 바이트 코드 낭비이며 성능에 부정적인 영향을 미친다는 것이 었습니다.


답변

나는 실제 이유에 대한 내부 정보에 대해 잘 알지 못하지만 의심은 매우 간단합니다. Scala는 프로그래머가 자연스럽게 for-comprehension을 선호하도록 부작용 루프를 사용하기 어렵게 만듭니다.

여러 가지 방법으로이를 수행합니다. 예를 들어, for변수를 선언하고 변경하는 루프 가 없습니다 . while조건을 테스트하는 동시에 루프에서 상태를 (쉽게) 변경할 수 없습니다. 즉, 바로 앞과 끝에서 돌연변이를 반복해야하는 경우가 많습니다. while블록 내에서 선언 된 변수는 while테스트 조건 에서 볼 수 없으므로 do { ... } while (...)훨씬 유용 하지 않습니다 . 등등.

해결 방법 :

while ({bytesRead = in.read(buffer); bytesRead != -1}) { ...

가치가 무엇이든간에.

대체 설명으로 Martin Odersky는 그러한 사용으로 인해 발생하는 몇 가지 매우 추악한 버그에 직면해야했으며 그의 언어에서이를 금지하기로 결정했습니다.

편집하다

David Pollack 은 몇 가지 실제 사실로 답변 했으며 Martin Odersky 자신이 자신의 답변에 대해 언급 한 사실에 의해 분명하게 입증 되었으며 Pollack이 제기 한 성능 관련 문제에 대한 신뢰를 제공합니다.


답변

이것은보다 “공식적으로 올바른”유형 시스템을 갖는 Scala의 일부로 발생했습니다. 공식적으로 말하면 할당은 순전히 부작용이있는 진술이므로을 반환해야합니다 Unit. 이것은 좋은 결과를 가져옵니다. 예를 들면 :

class MyBean {
  private var internalState: String = _

  def state = internalState

  def state_=(state: String) = internalState = state
}

state_=메소드가 리턴 Unit(같은 세터 예상되는) 정확하게 할당 반환하기 때문에 Unit.

스트림 복사와 같은 C 스타일 패턴의 경우이 특정 디자인 결정이 약간 번거로울 수 있다는 데 동의합니다. 그러나 실제로는 일반적으로 비교적 문제가 없으며 실제로 유형 시스템의 전반적인 일관성에 기여합니다.


답변

아마도 이것은 명령 쿼리 분리 원칙 때문일까요?

CQS는 OO와 함수형 프로그래밍 스타일의 교차점에서 인기있는 경향이 있는데, 이는 부작용이 있거나없는 (즉, 객체를 변경하는) 객체 메소드간에 명백한 차이를 생성하기 때문입니다. 변수 할당에 CQS를 적용하면 평소보다 더 많이 걸리지 만 동일한 아이디어가 적용됩니다.

CQS가 유용한 이유의 짧은 그림 : 기호가있는 가상 하이브리드 F / OO 언어 고려 List방법이 클래스 Sort, Append, First,와 Length. 명령형 OO 스타일에서는 다음과 같은 함수를 작성할 수 있습니다.

func foo(x):
    var list = new List(4, -2, 3, 1)
    list.Append(x)
    list.Sort()
    # list now holds a sorted, five-element list
    var smallest = list.First()
    return smallest + list.Length()

보다 기능적인 스타일에서는 다음과 같이 작성할 가능성이 높습니다.

func bar(x):
    var list = new List(4, -2, 3, 1)
    var smallest = list.Append(x).Sort().First()
    # list still holds an unsorted, four-element list
    return smallest + list.Length()

이것들은 똑같은 일을 하려고 하는 것 같지만 , 분명히 둘 중 하나가 틀렸고, 메소드의 동작에 대해 더 많이 알지 못한다면 어떤 것을 말할 수 없습니다.

CQS를 사용하지만, 우리는 경우에 주장 할 AppendSort따라서 때 우리가해서는 안 두 번째 양식을 사용하여 버그를 만들기에서 우리를 방지 목록을 변경, 그들은 단위 유형을 반환해야합니다. 따라서 부작용의 존재는 메서드 시그니처에도 암시 적으로 포함됩니다.


답변

나는 이것이 프로그램 / 언어에 부작용이 없도록 유지하기위한 것이라고 생각합니다.

당신이 설명하는 것은 일반적으로 나쁜 것으로 간주되는 부작용의 의도적 인 사용입니다.


답변

부울 표현식으로 할당을 사용하는 것은 가장 좋은 스타일이 아닙니다. 동시에 두 가지 작업을 수행하면 종종 오류가 발생합니다. 그리고 “==”대신 “=”의 우발적 인 사용은 Scalas 제한으로 방지됩니다.


답변

그건 그렇고 : 나는 자바에서도 초기 while-trick 어리 석음을 발견합니다. 왜 이런 식으로하지 않습니까?

for(int bytesRead = in.read(buffer); bytesRead != -1; bytesRead = in.read(buffer)) {
   //do something 
}

물론 할당은 두 번 나타나지만 최소한 bytesRead는 그것이 속한 범위에 있으며 재미있는 할당 트릭을 가지고 노는 것이 아닙니다 …