Вот нетипизированное лямбда-исчисление, члены которого индексируются по их свободным переменным. Я использую библиотеку singletons
для одноточечных значений строк уровня.Почему GHC не уменьшит мою семью?
{-# 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)
A Var
вводит свободную переменную. Абстракция лямбда связывает переменную, которая кажется свободной в теле (если есть та, которая соответствует). Приложения объединяют свободные переменные двух частей выражения, удаляя дубликаты (поэтому свободные переменные x y
: x
и y
, а свободные переменные 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 '[]
Итак: Почему не GHC уменьшить Remove "x" '["x"]
к '[]
, когда я запросить тип моего GADT? В общем, когда воля-или-не будет проверять тип семейства типов? Есть ли эвристика, которую я могу использовать, чтобы не удивляться ее поведению?
Я прочитал «Почему не GHC уменьшить мою семью? «И это звучало очень жестоко. –
@JoachimBreitner Даже лучшие компиляторы не могут делать все, что вы хотите, чтобы они –
Я подозревал бы совпадающие определения в 'Remove', на основе https://wiki.haskell.org/GHC/Type_families#Closed_family_simplification. Вам, вероятно, понадобится ограничение, свидетельствующее о том, что типы являются неравенскими. – phadej