예, 표면에 대한 매우 간단한 질문입니다. 그러나 끝까지 생각하는 데 시간이 걸리면 헤아릴 수없는 유형 이론의 깊이에 빠지게됩니다. 그리고 유형 이론도 당신을 응시합니다.
먼저, F #에 형식 클래스가 없다는 것을 이미 올바르게 이해했습니다. 이것이 바로 그 이유입니다. 그러나 당신은 인터페이스를 제안합니다 Mappable
. 좋아, 살펴 보자.
이러한 인터페이스를 선언 할 수 있다고 가정 해 봅시다. 그것의 서명이 어떻게 보일지 상상할 수 있습니까?
type Mappable =
abstract member map : ('a -> 'b) -> 'f<'a> -> 'f<'b>
f
인터페이스를 구현하는 유형은 어디에 있습니까 ? 아 잠깐만! F #도 마찬가지입니다! 여기 f
에 더 높은 종류의 변수가 있으며 F #에는 더 높은 종류가 없습니다. 함수 f : 'm<'a> -> 'm<'b>
나 이와 비슷한 것을 선언 할 방법이 없습니다 .
그러나 우리가 그 장애물을 극복했다고 가정 해 봅시다. 그리고 지금 우리는 인터페이스가 Mappable
구현 될 수있다 List
, Array
, Seq
, 부엌 싱크대. 하지만 기다려! 이제 우리는이 방법 대신 기능을하고, 방법을 잘 구성하지 않습니다! 중첩 목록의 모든 요소에 42를 추가하는 방법을 살펴 보겠습니다.
// Good ol' functions:
add42 nestedList = nestedList |> List.map (List.map ((+) 42))
// Using an interface:
add42 nestedList = nestedList.map (fun l -> l.map ((+) 42))
보세요 : 이제 람다 식을 사용해야합니다! 이 .map
구현을 다른 함수에 값 으로 전달할 수있는 방법이 없습니다 . 효과적으로 "값으로 기능"의 끝 (그리고 예, 람다를 사용하는 것은이 예에서별로 나쁘지 않지만 나를 믿습니다. 매우 추악합니다)
그러나 우리는 아직 끝나지 않았습니다. 메서드 호출이므로 형식 유추가 작동하지 않습니다! .NET 메서드의 형식 서명은 개체의 형식에 따라 다르므로 컴파일러가 두 형식을 모두 유추 할 방법이 없습니다. 이것은 실제로 초보자가 .NET 라이브러리와 상호 운용 할 때 발생하는 매우 일반적인 문제입니다. 유일한 치료법은 형식 서명을 제공하는 것입니다.
add42 (nestedList : #Mappable) = nestedList.map (fun l -> l.map ((+) 42))
아, 그러나 이것은 여전히 충분하지 않습니다! nestedList
자체 서명을 제공했지만 람다의 매개 변수에 대한 서명을 제공하지 않았습니다 l
. 그러한 서명은 무엇이어야합니까? 당신이해야한다고 말 하시겠습니까 fun (l: #Mappable) -> ...
? 아, 그리고 이제 우리는 마침내 N 등급을 얻었 #Mappable
습니다. "모든 유형 'a
과 같은 유형"에 대한 지름길입니다 'a :> Mappable
.
또는 대안으로 우리는 더 높은 친절로 돌아가서 nestedList
더 정확하게 유형을 선언 할 수 있습니다 .
add42 (nestedList : 'f<'a<'b>> where 'f :> Mappable, 'a :> Mappable) = ...
그러나 이제는 형식 추론을 제쳐두고 람다 식으로 돌아가서 map
다른 함수에 값으로 전달할 수없는 방법을 살펴 보겠습니다 . Elm이 레코드 필드로 수행하는 것과 같은 것을 허용하도록 구문을 약간 확장한다고 가정 해 봅시다.
add42 nestedList = nestedList.map (.map ((+) 42))
유형은 무엇입니까 .map
? Haskell에서와 같이 제한 유형 이어야합니다 !
.map : Mappable 'f => ('a -> 'b) -> 'f<'a> -> 'f<'b>
와우 .NET이 이러한 유형의 존재조차 허용하지 않는다는 사실을 제쳐두고 효과적으로 유형 클래스를 다시 얻었습니다!
그러나 F #에 먼저 유형 클래스가없는 이유가 있습니다. 그 이유 중 많은 부분이 위에서 설명되었지만, 더 간결한 방법은 다음과 같습니다. 단순성 .
보시다시피, 이것은 원사의 공입니다. 유형 클래스가 있으면 제약 조건, 높은 종류, 순위 N (또는 적어도 순위 2)을 가져야하며 알기 전에 Impredicative 유형, 유형 함수, GADT 및 모든 것을 요구합니다. 나머지.
그러나 Haskell은 모든 상품에 대해 가격을 지불합니다. 모든 것을 추론하는 좋은 방법이 없다는 것이 밝혀졌습니다 . 더 높은 종류의 유형은 작동하지만 제약 조건은 이미 작동하지 않습니다. Rank-N-꿈도 꾸지 마 그리고 그것이 작동하더라도, 당신은 이해해야하는 PhD가 있어야하는 유형 오류가 발생합니다. 하스켈에서 당신이 부드럽게하고 이유입니다 장려 모두에 유형 서명을 넣어. 글쎄, 모든 것이 아니라 거의 모든 것. 그리고 타입 시그니처 (예 : inside let
및 where
)를 넣지 않는 경우 -놀랍게도, 그 장소는 실제로 단형 화되어 있으므로 단순한 F # 랜드로 돌아갑니다.
반면 F #에서는 형식 서명이 거의 없으며 대부분 문서 나 .NET interop에만 사용됩니다. 이 두 가지 경우를 제외하고는 F #으로 전체 복잡한 프로그램을 작성하고 형식 서명을 한 번만 사용할 수는 없습니다. 형식 추론은 처리하기에 너무 복잡하거나 모호한 것이 없기 때문에 제대로 작동합니다.
그리고 이것은 Haskell에 비해 F #의 큰 장점입니다. 예, Haskell을 사용하면 매우 복잡한 방법을 매우 정확하게 표현할 수 있습니다. 그러나 F #을 사용하면 거의 파이썬이나 루비와 같이 매우 욕구가 많으며, 넘어 질 경우 컴파일러에서 계속 잡아낼 수 있습니다.