2016-10-25 5 views
9

other question이 복제물로 폐쇄되었으므로 다시 시도하겠습니다. 나는 또한 this question을 읽었으며 내가 묻는 것은 다르다. 나는 () => Type과 다른 Call-by-Name: => Type의 내부 구현을 배우는 데 관심이 있습니다.스칼라가() => T와 => T를 구별하는 방법 T

두 경우에 차이가없는 javap 및 cfr 디스 어셈블리를 보면 혼란스러워하고 있습니다.

예컨대 ParamTest.scala :

object ParamTest { 
    def bar(x: Int, y: => Int) : Int = if (x > 0) y else 10 
    def baz(x: Int, f:() => Int) : Int = if (x > 0) f() else 20 
} 

javap ParamTest.scala은 javap 출력 :

public final class ParamTest { 
    public static int baz(int, scala.Function0<java.lang.Object>); 
    public static int bar(int, scala.Function0<java.lang.Object>); 
} 

CFR 디 컴파일 출력java -jar cfr_0_118.jar ParamTest$.class :

import scala.Function0; 

public final class ParamTest$ { 
    public static final ParamTest$ MODULE$; 

    public static { 
     new ParamTest$(); 
    } 

    public int bar(int x, Function0<Object> y) { 
     return x > 0 ? y.apply$mcI$sp() : 10; 
    } 

    public int baz(int x, Function0<Object> f) { 
     return x > 0 ? f.apply$mcI$sp() : 20; 
    } 

    private ParamTest$() { 
     MODULE$ = this; 
    } 
} 

EDIT 1 : 스칼라 구문 트리 : 2scalac -Xprint:parse ParamTest.scala

package <empty> { 
    object ParamTest extends scala.AnyRef { 
    def <init>() = { 
     super.<init>(); 
    () 
    }; 
    def bar(x: Int, y: _root_.scala.<byname>[Int]): Int = if (x.$greater(0)) 
     y 
    else 
     10; 
    def baz(x: Int, f: _root_.scala.Function0[Int]): Int = if (x.$greater(0)) 
     f() 
    else 
     20 
    } 
} 

편집 : 메일 링리스트의 연구 :

은 본질적으로 => T() => T로 구현되는 내용의 메일 링리스트에이 interesting post을 읽어보십시오. 인용구 :이가 "로 이름 매개 변수"라고되어 있지만

첫째, 그것은 실제로 Function0로 구현,

f: => Boolean

f:() => Boolean

양쪽 끝에 다른 구문이 사용되었습니다.

이제 두 개가 다른 것으로 명시된 this answer에 의해 더욱 혼란스러워집니다.

질문 :

  • 어떻게 스칼라 baz에서 bar을 구별합니까? 두 가지에 대한 메소드 서명 (구현이 아님)은 디 컴파일 된 코드에서 동일합니다.
  • 두 시나리오의 차이점이 컴파일 된 바이트 코드에 유지되지 않습니까?
  • 디 컴파일 된 코드가 부정확합니까?
  • 편집 한 후 : 나는 scalac 구문 트리가 차이를 보이지 않는 것으로, bar 유형 _root_.scala.<byname>[Int]의 두 번째 인수가 있습니다. 그것은 무엇을합니까?모든 설명, 포인터는 scala source 또는 이와 동등한 의사 코드가 유용합니다.
  • 위의 편집 2 참조 : 따옴표 붙은 블록이 맞습니까? 에서와 같이 => TFunction0의 특수 하위 클래스입니까?
+2

왜 이러한 유형이 JVM 바이트 코드로 표현 되는가가 중요한 이유는 무엇입니까? 그것들은 스칼라 코드가 다르다. 중요한 것은 (자바에서 스칼라를 호출하지 않는 한, 당신이 요구하는 것이 아니다). –

+0

스칼라는 그런 식으로 작동하기 때문입니다. 스칼라 코드를 .class 파일로 컴파일하고 JVM에서 실행합니다. 따라서 .class 파일에는 필요하고 충분한 정보가 있어야합니다. 스칼라 소스 파일은 전혀 중요하지 않습니다. [연결된 질문] (http://stackoverflow.com/questions/40246137/what-does-double-right-arrow-type-with-no-lhs-mean-in-function-argument/)을 참조하십시오. 'ParamTest.scala' 소스 파일을 삭제 한 후'foo (baz, 100)'호출 오류. – vsnyc

+0

이 질문에 대한 또 다른 동기는 [이 답변] (http://stackoverflow.com/a/13337382/2063026)입니다. 가장 인상 된 의견 (160)이 주장하는 바에 따르면, " '=> Int'는'Int'와는 다른 타입으로, Int를 생성하는 인자가없는 함수입니다. 그러나 우리가 위에서 보았 듯이 그것은 사실이 아닙니다. call-by-name은 게으른 평가와 관련이 있으며, 아무런 인자도없는 함수가 아닙니다 ... – vsnyc

답변

4

스칼라가 어떻게 구별합니까 barbaz입니까? 두 가지에 대한 메소드 서명 ( 구현이 아님)은 디 컴파일 된 코드에서 동일합니다.

스칼라는 사이에을 구별 할 필요가 없습니다. 그것의 관점에서, 이들은 두 가지 다른 방법입니다. 흥미로운 (나에게하는 것은 적어도) 우리가 barbaz의 이름을 변경하고 "통화로 이름"매개 변수 과부하를 만들려고하면, 우리가 얻을 수 있다는 것입니다 : 우리가 그것에 대해 힌트입니다

Error:(12, 7) double definition: 
method bar:(x: Int, f:() => Int)Int and 
method bar:(x: Int, y: => Int)Int at line 10 
have same type after erasure: (x: Int, f: Function0)Int 
    def bar(x: Int, f:() => Int): Int = if (x > 0) f() else 20 

커버 아래에 무언가가 번역되어 Function0으로 번역됩니다.

컴파일 된 바이트 코드에 두 시나리오의 차이가 지속되지 않습니까?

스칼라가 JVM 바이트 코드를 방출하기 전에는 컴파일 단계가 추가됩니다.

[[syntax trees at end of uncurry]] 
package testing { 
    object ParamTest extends Object { 
    def <init>(): testing.ParamTest.type = { 
     ParamTest.super.<init>(); 
    () 
    }; 
    def bar(x: Int, y:() => Int): Int = if (x.>(0)) 
     y.apply() 
    else 
     10; 
    def baz(x: Int, f:() => Int): Int = if (x.>(0)) 
     f.apply() 
    else 
     20 
    } 
} 

우리가 바이트 코드를 방출하기 전에도, barFunction0로 번역 :이 경우 흥미로운 하나는 "uncurry"단계 (-Xprint:uncurry) 보는 것입니다.

은 확실히 정확,

없음 부정확 디 컴파일 코드입니다.

내가 scalac 구문 트리가 차이를 보이지 않는 것으로, 바 을 가지고 유형 루트 .scala의 두 번째 인수. [지능]. 그것은 무엇입니까 합니까?

스칼라 컴파일은 단계별로 수행되며, 각 단계 출력은 다음 단계의 입력입니다. 구문 분석 된 AST 외에도 스칼라 단계는 기호를 작성하여 한 단계가 특정 구현 세부 사항에 의존하면 사용할 수 있도록합니다.<byname>은이 메소드가 "call-by-name"을 사용하여 단계 중 하나가이를 볼 수 있고 그것에 대해 뭔가 할 수 있도록하는 컴파일러 심볼입니다.

+0

매우 도움이됩니다. 답변을 주시고 올바른 방향으로 안내해 주셔서 감사합니다. – vsnyc

+2

@vsnyc 환영합니다. –

+0

위대한, 좋은 대답 – Ashesh

3

스칼라 코드는 컴파일러에서 분석하여 jvm 바이트 코드로 변환됩니다. 스칼라 레벨에서는 implicits, 매우 강력한 타입 시스템, 이름 매개 변수 및 기타 이와 같은 호출이 있습니다. 바이트 코드에서 그것은 모두 사라졌습니다. 아니 카레 매개 변수, 암시, 그냥 일반 방법. 런타임은 () => A=> A을 구별 할 필요가 없으며 단지 바이트 코드를 실행합니다. 모든 검사 및 검증 오류는 바이트 코드가 아닌 스칼라 코드를 분석하는 컴파일러에서 발생합니다. 컴파일 과정에서 이름은 Function0으로 바뀌 었으며 이러한 매개 변수의 모든 용도에는 apply 메서드가 호출되었지만 구문 분석 단계에서는 발생하지 않지만 나중에 컴파일러 출력에 <byname>이 표시됩니다. 후기 단계를 살펴보십시오.

3

스칼라가 그렇게 작동하기 때문에. 스칼라 코드를 .class 파일로 컴파일하고 JVM에서 실행합니다. 따라서 .class 파일에는 필요하고 충분한 정보가 있어야합니다.

는 않습니다. 이 정보는 @ScalaSignature이라는 주석에 저장됩니다. javap -v은 그 존재를 보여 주어야하지만 사람이 읽을 수는 없습니다.

은 JVM 바이트 코드로 표현할 수없는 수 스칼라 서명에 많은 정보가 있기 때문에 필요하다 : 단지로 이름 Function0 매개 변수 대,하지만 등 액세스 한정자, 매개 변수 이름,

+0

Yuval의 대답은 매우 도움이되었습니다, 그것은 올바른 방향으로 나를 인도했습니다. 나는 지금 더 많은 것을 읽고 있습니다 [ 이 대답] (http : // stackoverflow.co.kr/a/3312036/2063026)에서 [VonC] (http://stackoverflow.com/users/6309/vonc)를 참조하십시오. – vsnyc

+1

꽤 오래된 스칼라 버전에만 직접 적용된다는 점에 유의하십시오. Scala 2.8의'ScalaSig' 속성은'@ ScalaSignature'로 대체되었습니다. –

+0

감사합니다. 나는 그것을 메모 할 것입니다. – vsnyc

관련 문제