code

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

codestyles 2020. 9. 25. 07:53
반응형

할당 된 값이 아닌 단위로 평가하는 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()

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

Using CQS, however, we would insist that if Append and Sort alter the list, they must return the unit type, thus preventing us from creating bugs by using the second form when we shouldn't. The presence of side effects therefore also becomes implicit in the method signature.


I'd guess this is in order to keep the program / the language free of side effects.

What you describe is the intentional use of a side effect which in the general case is considered a bad thing.


It is not the best style to use an assignment as a boolean expression. You perform two things at the same time which leads often to errors. And the accidential use of "=" instead of "==" is avoided with Scalas restriction.


By the way: I find the initial while-trick stupid, even in Java. Why not somethign like this?

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

Granted, the assignment appears twice, but at least bytesRead is in the scope it belongs to, and I'm not playing with funny assignment tricks...


You can have a workaround for this as long as you have a reference type for indirection. In a naïve implementation, you can use the following for arbitrary types.

case class Ref[T](var value: T) {
  def := (newval: => T)(pred: T => Boolean): Boolean = {
    this.value = newval
    pred(this.value)
  }
}

Then, under the constraint that you’ll have to use ref.value to access the reference afterwards, you can write your while predicate as

val bytesRead = Ref(0) // maybe there is a way to get rid of this line

while ((bytesRead := in.read(buffer)) (_ != -1)) { // ...
  println(bytesRead.value)
}

and you can do the checking against bytesRead in a more implicit manner without having to type it.

참고URL : https://stackoverflow.com/questions/1998724/what-is-the-motivation-for-scala-assignment-evaluating-to-unit-rather-than-the-v

반응형