为什么GHC不会减少我的类型家庭?

时间:2015-07-16 20:25:03

标签: haskell lambda-calculus gadt type-families

这是一个无类型的lambda演算,其术语由其自由变量索引。我正在使用singletons库来获取类型级别字符串的单例值。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

import Data.Singletons
import Data.Singletons.TypeLits

data Expr (free :: [Symbol]) where
    Var :: Sing a -> Expr '[a]
    Lam :: Sing a -> Expr as -> Expr (Remove a as)
    App :: Expr free1 -> Expr free2 -> Expr (Union free1 free2)

Var引入了一个自由变量。 lambda抽象绑定一个在体内看起来是自由的变量(如果有匹配的那个)。应用程序连接表达式两个部分的自由变量,删除重复项(因此x y的自由变量为xy,而x x的自由变量只是x)。我写出了那些类型的家庭:

type family Remove x xs where
    Remove x '[] = '[]
    Remove x (x ': xs) = Remove x xs
    Remove x (y ': xs) = y ': Remove x xs

type family Union xs ys where
    Union xs ys = Nub (xs :++ ys)

type family xs :++ ys where
    '[] :++ ys = ys
    (x ': xs) :++ ys = x ': (xs :++ ys)

type family Nub xs where
    Nub xs = Nub' '[] xs

type family Nub' seen xs where
    Nub' seen '[] = '[]
    Nub' seen (x ': xs) = If (Elem x seen) (Nub' seen xs) (Nub' (x ': seen) (x ': xs))

type family If c t f where
    If True t f = t
    If False t f = f

type family Elem x xs where
    Elem x '[] = False
    Elem x (x ': xs) = True
    Elem x (y ': xs) = Elem x xs

我在互动提示下对此进行了测试:

ghci> :t Var (sing :: Sing "x")
Var (sing :: Sing "x") :: Expr '["x"]  -- good
ghci> :t (Lam (sing :: Sing "x") (Var (sing :: Sing "x")))
(Lam (sing :: Sing "x") (Var (sing :: Sing "x")))
  :: Expr (Remove "x" '["x"])  -- not so good

我很惊讶地发现身份函数\x. x的类型是Expr (Remove "x" '["x"]),而不是Expr '[]。 GHC似乎不愿意评估类型族Remove。 我进行了一些实验,并了解到我的类型系列本身并不是问题 - 在这种情况下,GHC很乐意减少它:

ghci> :t (Proxy :: Proxy (Remove "x" '["x"]))
(Proxy :: Proxy (Remove "x" '["x"])) :: Proxy '[]

那么:当我查询GADT的类型时,为什么GHC不会将Remove "x" '["x"]减少到'[]?一般情况下,类型检查器何时或不会评估类型系列?我可以使用启发式方法来避免对其行为感到惊讶吗?

1 个答案:

答案 0 :(得分:2)

有效。 GHC似乎只是懒惰。

λ *Main > :t (Lam (Proxy :: Proxy "x") (Var (Proxy :: Proxy "x")))
(Lam (Proxy :: Proxy "x") (Var (Proxy :: Proxy "x")))
  :: Expr (Remove "x" '["x"])

λ *Main > :t (Lam (Proxy :: Proxy "x") (Var (Proxy :: Proxy "x"))) :: Expr '[]
(Lam (Proxy :: Proxy "x") (Var (Proxy :: Proxy "x"))) :: Expr '[]
  :: Expr '[]

λ *Main > :t (Lam (Proxy :: Proxy "x") (Var (Proxy :: Proxy "x"))) :: Expr '["x"]

<interactive>:1:2:
    Couldn't match type ‘'[]’ with ‘'["x"]’
    Expected type: Expr '["x"]
      Actual type: Expr (Remove "x" '["x"])
    In the expression:
        (Lam (Proxy :: Proxy "x") (Var (Proxy :: Proxy "x"))) ::
          Expr '["x"]

我更改了定义,因此不依赖于单例库(更容易在ad-hoc中测试):

{-# LANGUAGE TypeOperators, DataKinds, TypeFamilies, GADTs #-}

import Data.Proxy
import GHC.TypeLits

type family Remove (x :: Symbol) (xs :: [Symbol]) where
    Remove x '[] = '[]
    Remove x (x ': xs) = Remove x xs
    Remove x (y ': xs) = y ': Remove x xs

data Expr (free :: [Symbol]) where
    Var :: KnownSymbol a => Proxy a -> Expr '[a]
    Lam :: KnownSymbol a => Proxy a -> Expr as -> Expr (Remove a as)
--    App :: Expr free1 -> Expr free2 -> Expr (Union free1 free2)