请考虑以下代码:
{-# LANGUAGE TypeFamilies #-}
data Twothings a b = Twothings a b
type family Leftthing a where
Leftthing (Twothings a b) = Leftthing a
Leftthing a = a
leftthing :: a -> Leftthing a
leftthing (Twothings a b) = leftthing a
leftthing b = b
它无法编译,出现以下错误:
Couldn't match expected type ‘a’
with actual type ‘Twothings a0 t0’
‘a’ is a rigid type variable bound by
the type signature for:
leftthing :: forall a. a -> Leftthing a
它抱怨行leftthing (Twothings a b) = leftthing a
。如果我理解正确,它不能将类型签名中的类型变量a
与构造函数Twothings
的类型统一起来。好吧,这似乎有道理。但是,如何在类型签名中定义类型系列的函数?
答案 0 :(得分:11)
宣布
时leftthing :: a -> Leftthing a
您说leftthing
的来电可以选择a
。
然后写下
leftthing (Twothings a b) = leftthing a
您假设他们选择了Twothings
类型,并且不一定如此,您的程序将被拒绝。
您可能认为您正在测试他们是否选择了Twothings
类型,但没有!类型信息在运行时被删除,因此无法进行此类测试。
您可以尝试恢复必要的运行时信息。首先,让我修复您的Leftthing
和leftthing
之间的不一致。
type family Leftthing a where
Leftthing (Twothings a b) = Leftthing{-you forgot the recursion!-} a
Leftthing a = a
现在我们可以将证人的GADT定义为Twothing
ness。
data IsItTwothings :: * -> * where
YesItIs :: IsItTwothings a -> IsItTwothings (Twothings a b)
NoItIsn't :: Leftthing a ~ a => IsItTwothings a
-- ^^^^^^^^^^^^^^^ this constraint will hold for any type
-- which is *definitely not* a Twothings type
然后我们可以将证人作为论据传递:
leftthing :: IsItTwothings a -> a -> Leftthing a
leftthing (YesItIs r) (Twothings a b) = leftthing r a
leftthing NoItIsn't b = b
实际上,见证是您类型根目录中左嵌套Twothings
es数量的一元编码。这足以让您在运行时确定正确的拆包数量。
> leftthing (YesItIs (YesItIs NoItIsn't)) (Twothings (Twothings True 11) (Twothings "strange" [42]))
True
总而言之,您无法通过模式匹配来找出值。相反,您需要知道要进行模式匹配的类型(因为类型决定了内存布局,并且没有运行时类型标记)。你不能直接在类型上进行模式匹配(因为它们并不是匹配的)。您可以构造数据类型,这些数据类型充当类型结构的运行时证据,而不是匹配它们。
也许有一天,如果你给它类型
,你的程序将会起作用leftthing :: pi a. a -> Leftthing a
其中pi
是依赖量词,表示隐藏类型参数未被删除,而是在运行时传递和匹配。那天还没到,但我认为它会。