2013-04-05 4 views
12

OCaml의에서, .mli에 가지고 법적 :η-확장

val f : 'a -> 'a 
val g : 'a -> 'a 

.ml : F 번호에 그러나

let f x = x 
let g = f 

이 거부됩니다 :

eta_expand.ml(2,5): error FS0034: Module 'Eta_expand' contains 
    val g : ('a -> 'a)  
but its signature specifies 
    val g : 'a -> 'a  

The arities in the signature and implementation differ. The signature specifies that 'g' is function definition or lambda expression accepting at least 1 argument(s), but the implementation is a computed function value. To declare that a computed function value is a permitted implementation simply parenthesize its type in the signature, e.g. 
    val g: int -> (int -> int) 
instead of 
    val g: int -> int -> int. 

한 가지 해결 방법은 g의 정의를 η 확장하는 것입니다.

let g x = f x 

내 코드가 실제로 (이 동일해야 (등 예외없이, 부작용,) 순수하게 기능적인 경우, 언어 유형을 일반화하는 방법에 따라, 다형성에 대한 더 나은 수 있습니다 : OCaml의에서 부분적인 애플리케이션은 다형 함수를 생성하지 않지만 η 확장은 그렇게한다.

체계적인 η 확장에 결점이 있습니까?

η 확장에 대한 두 가지 대답은 다음과 같습니다 .-) 대신 내 기능 유형 주변에 괄호를 추가하는 것이 좋습니다. 이는 분명히 F #이 입력 수준에서 함수의 "참"정의 (부분 응용에서와 같이 λ- 표현식과 계산 된 정의)를 구별하기 때문입니다. 아마도 λ- 표현식이 CLR 함수에 직접 매핑되고 계산 된 정의가 객체를 위임하기 때문에 매핑되는 것 같습니다. (나는이 해석 모르겠어요 및 F 번호에 대해 잘 알고 사람이 문서는이 기술 참조를 가리 수 있다면 감사하겠습니다.)

해결책 체계적으로 .mli모든 기능 유형을 괄호를 추가하는 것입니다 만, 나는 이것이 비효율로 이어질 수 있다는 것을 두려워한다. 다른 하나는 계산 된 함수를 감지하고 해당 유형을 .mli에 괄호로 묶는 것입니다. 세 번째 해법은 명백한 경우를 η 확장하고 다른 것은 괄호로 묶는 것이다.

중요한 성능이나 페널티를 유발하는 항목을 측정하기에 F #/CLR 내부에 익숙하지 않습니다.

+2

그냥 val g : ('a ->'a)'로 지정하십시오. F # 유형 시스템의 알려진 기능/버그입니다. –

+0

다시 말하면, "그냥 만드십시오"- 아마도 λ- 표현식과 계산 된 함수가 상호 교환 가능한 타입을 가지고 있지 않다면 그것은 좋은 이유입니다. 게다가 이것은 자동 생성 된 코드이기 때문에 문제는 단지 몇 개의 괄호를 수동으로 추가하는 것보다 조금 더 복잡합니다 ... –

+0

이 경우 두 개의 차이점이 있습니다. 하나는 메소드로 컴파일되고 다른 하나는 정적' Func '속성. 호환성은 양방향 적이 지 않습니다 (즉,'a -> b : <: (a -> b)''(a -> b) : <: a -> b')가 아닙니다. –

답변

4

흥미롭게도 내 fsi가 더 도움이 오류 메시지가 있습니다 : 당신이 g :('a -> 'a) 모두를 얻기 위해 괄호를 추가하는 경우

/test.fs(2,5): error FS0034: Module 'Test' contains 
    val g : ('a -> 'a) but its signature specifies 
    val g : 'a -> 'a The arities in the signature and implementation differ. 
      The signature specifies that 'g' is function definition or lambda expression 
      accepting at least 1 argument(s), but the implementation is a computed 
      function value. To declare that a computed function value is a permitted 
      implementation simply parenthesize its type in the signature, e.g. 
     val g: int -> (int -> int) instead of 
     val g: int -> int -> int. 

을 이론적으로

+1

나는 간결함을 위해 오류 메시지의 끝을 잘랐다. 나는 "모두 괜찮습니다"라는 것에 동의하지 않습니다. :-) –

8

괜찮은 F # 기능 유형 'a -> 'b -> 'c'a -> ('b -> 'c)와 같은 유형입니다. 즉, 다중 인수 함수는 F #에서 카레 된 형식을 사용하여 표현됩니다. 대부분의 경우 다른 하나가 예상되는 위치를 사용할 수 있습니다. 고차 함수를 호출 할 때.

그러나 실용적인 이유에서 F # 컴파일러는 실제로 형식을 구분합니다. 동기 부여는 컴파일 된 .NET 코드에서 다르게 표현된다는 것입니다. 이것은 성능 및 C#과의 상호 운용성에 영향을 미치므로이를 구별하는 것이 유용합니다.

Foo : int -> int -> int가 회원 int Foo(int, int)로 컴파일 될 것입니다 함수 - 컴파일러는 기본적으로 카레 양식을 사용하지 않는,이 모두 인수 Foo (일반적인 경우)를 호출 할 때 더 효율적이고 있기 때문에 상호 운용성에 대한 더 .Bar : int -> (int -> int) 함수는 FSharpFunc<int, int> Bar(int)으로 컴파일됩니다. 실제로는 카레 드 양식을 사용합니다 (따라서 단일 매개 변수로 호출하는 것이 더 효율적이며 C#에서 사용하기가 어렵습니다).

이것은 F #이 서명과 관련하여 형식을 동일하게 취급하지 않는 이유입니다. 서명은 형식을 지정하지만 여기서도 함수를 컴파일하는 방법을 지정합니다. 구현 파일은 올바른 유형의 함수를 제공해야하지만,이 경우에는 올바른 컴파일 형식의 함수를 제공해야합니다.

+0

차이점을 이해합니다. 내가 가장 잘 알고있는 해결 방법은 다음과 같습니다. (a) η- 모든 계산 된 함수를 확장해야합니까? (b) 모든 계산 된 함수의 형식을 괄호로 묶어야합니까? –

+0

@monniaux η- 모든 계산 된 함수를 확장하는 것이 내 기본 선택 일 것입니다. 그러나 그것은 여러 가지 요인에 달려 있습니다. C# 상호 운용성을 확실히 원한다면 (a) 계산 된 함수에서 부분적으로 응용 프로그램을 자주 사용하면 (b)보다 나은 성능을 얻을 수 있습니다. –