2016-07-07 5 views
3

Core의 구조를보다 잘 이해하기 위해 Core Haskell 용 커스텀 예쁜 프린터를 작성했습니다. 이 예쁜 프린터의 주안점은 CoreModule이며 출력에 데이터 생성자가 포함되어 있으며 기본값은 Outputable 구현이 아닌 것으로 보입니다. 여기 코어 하스켈이 기능에 유형을 적용하는 것은 무엇을 의미합니까?

module Bar2 where 

add :: Int -> Int -> Int 
add a b = a + b 

add2 a b = a + b 

가 꽤 프린터 출력 : 여기에

난에 꽤 프린터를 실행하고 모듈의 코드

------------------------------- Module Metadata -------------------------------- 
Module { "main" :: modulePackageId, "Bar2" :: moduleName } 
-------------------------------- Type Bindings --------------------------------- 
[r0 :-> Identifier ‘add’, rjH :-> Identifier ‘add2’] 
-------------------------------- Core Bindings --------------------------------- 
NonRec (Id "add2") 
     (Lam (TyVar "a") 
      (Lam (Id "$dNum") 
       (Lam (Id "a1") 
         (Lam (Id "b") 
          (App (App (App (App (Var (Id "+")) 
               (Type (TyVar (TyVar "a")))) 
              (Var (Id "$dNum"))) 
            (Var (Id "a1"))) 
           (Var (Id "b"))))))) 

NonRec (Id "add") 
     (Lam (Id "a") 
      (Lam (Id "b") 
       (App (App (App (App (Var (Id "+")) 
            (Type (TyConApp (Int) []))) 
           (Var (Id "$fNumInt"))) 
          (Var (Id "a"))) 
         (Var (Id "b"))))) 
--------------------------------- Safe Haskell --------------------------------- 
Safe 
------------------------------------- End -------------------------------------- 

무엇을 나에게 혼란 것은 모두 그 인스턴스의 경우 Core는 형식 변수 또는 형식 생성자를 + 함수에 적용하는 것처럼 보입니다. 인수는 인수하기 전에 $dNum 또는 $fNumInt과 마찬가지입니다.

add 함수의 경우에도 형식을 명시 적으로 지정하고 add2은 컴파일러의 추론을받습니다. 이것은 또한 람다 함수 연쇄가 평가에 필요한 인수의 수에 영향을 미치는 것으로 보이며, add은 2가 필요하고 add2은 4를 필요로합니다.

이것은 모두 무엇을 의미합니까?

답변

7

코어는 꽤 많이 SystemF (기술적으로는 SystemFC)입니다. SystemF에서 유형 변수는 함수에 대한 인수 여야합니다. 당신의 예에서, 하스켈 add2TyVar "a" 인수를 설명하고 그

add2 :: Num a => a -> a -> a 
add2 a b = a + b 

있음을 유추합니다.

또한 하스켈은 ab의 형식이 무엇인지에 따라 Num 함수의 '올바른'집합으로 디스패치하는 방법을 찾아야합니다. 각 유형의 클래스 제약에 대해 사전 인수를 사용하면됩니다. 그것은 Id $dNum 인수입니다. add의 경우 Haskell은 해당 연산이 Int 인 것을 알고 있기 때문에 적절한 (+) 함수를 찾을 수있는 사전을 이미 알고 있습니다 (따라서 전달 될 필요가 없습니다 : 그냥 $fNumInt입니다).

본질적으로 두포에서 발생하는 것은 각각의 typeclass에 대해 하스켈이 typeclass 내의 함수 인 필드를 가진 data $d<Class> = ... 레코드를 만든다는 것입니다. 그런 다음 각 인스턴스에 대해 다른 $f<Class><Type> :: $d<Class>을 만듭니다.

는 코어와 유사하게,뿐만 아니라 하스켈 타입의 인수를 재생할 수 있습니다 GHC 8.x에서 Here is another excellent answer describing Core related things.

5

This is explained in more detail here. 다음은 게시 된 코드를 기반으로 한 주석이 추가 된 예제입니다. 이 유형 Int 작동되도록

add :: Int -> Int -> Int 
add a b = (+) @ Int a b 

(+) @ Int

다형성 (+) 연산자 전문.

코어에는 또한 $fNumInt 주위를 통과하는 typeclass 사전이 있습니다.

add2 :: forall n. Num n => n -> n -> n  
add2 a b = (+) @ n a b 

n을 알지 못한다는 점을 제외하면 기본적으로 동일합니다. 코어

add2이어서 형 (+) 인수로 전송된다 (혼동 등록한 예의 a라고, 즉 (Lam (TyVar "a") ...) 숨겨진 "타입 값"인수 n을 걸린다. 사전은 현재 알려지지 않았기 때문에 코어에는 또 다른 숨겨진 인수가 있습니다. 사전은 add2의 호출자가 전달해야하며 그 다음 (+)으로 전달됩니다. 이 추가 인수는 $dNum입니다 ((Lam (Id "$dNum") ... 참조).