Итак, я играл с DataKinds
и TypeFamilies
в Haskell и начал смотреть на генерируемый Core GHC.
Вот немного TestCase, чтобы мотивировать мой вопрос:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
module TestCase where
data Ty = TyBool | TyInt
type family InterpTy (t :: Ty) :: *
type instance InterpTy TyBool = Bool
type instance InterpTy TyInt = Int
data Expr (a :: Ty) where
I :: Int -> Expr TyInt
B :: Bool -> Expr TyBool
eval :: Expr a -> InterpTy a
eval (I i) = i
eval (B b) = b
Посмотрим на сгенерированное ядро для функции eval
TestCase.eval =
\ (@ (a_aKM :: TestCase.Ty)) (ds_dL3 :: TestCase.Expr a_aKM) ->
case ds_dL3 of _ [Occ=Dead] {
TestCase.I dt_dLh i_as6 ->
i_as6
`cast` (Sub (Sym TestCase.TFCo:R:InterpTyTyInt[0])
; (TestCase.InterpTy (Sym dt_dLh))_R
:: GHC.Types.Int ~# TestCase.InterpTy a_aKM);
TestCase.B dt_dLc b_as7 ->
b_as7
`cast` (Sub (Sym TestCase.TFCo:R:InterpTyTyBool[0])
; (TestCase.InterpTy (Sym dt_dLc))_R
:: GHC.Types.Bool ~# TestCase.InterpTy a_aKM)
}
Очевидно, нам нужно нести информацию о том, что a
может быть в конкретной ветки. Если я не индексирую Datakind и не буду использовать TypeFamilies, то это намного легче понять.
Это будет примерно так:
Main.eval =
\ (@ a_a1hg) (ds_d1sQ :: Main.Simpl a_a1hg) ->
case ds_d1sQ of _ [Occ=Dead] {
Main.I' dt_d1to i_aFa ->
i_aFa
`cast` (Sub (Sym dt_d1to) :: GHC.Integer.Type.Integer ~# a_a1hg);
Main.B' dt_d1tk b_aFb ->
b_aFb `cast` (Sub (Sym dt_d1tk) :: GHC.Types.Bool ~# a_a1hg)
}
Это я прекрасно понимаю, что меня беспокоит в примере TypeFamilies
:
(Sub (Sym TestCase.TFCo:R:InterpTyTyInt[0])
; (TestCase.InterpTy (Sym dt_dLh))_R
:: GHC.Types.Int ~# TestCase.InterpTy a_aKM);
Вторая строка - это то, что меня действительно смущает. Что такое точка с запятой? Кажется, это немного неуместно или я чего-то не хватает? Есть ли место, где я могу это прочитать (я с радостью беру документы, если вы можете порекомендовать его)
С уважением,
raichoo