为什么我不能在类型系列上进行模式匹配?

时间:2017-09-09 09:45:53

标签: haskell types

请考虑以下代码:

{-# 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的类型统一起来。好吧,这似乎有道理。但是,如何在类型签名中定义类型系列的函数

1 个答案:

答案 0 :(得分:11)

宣布

leftthing :: a -> Leftthing a

您说leftthing来电可以选择a

然后写下

leftthing (Twothings a b) = leftthing a

假设他们选择了Twothings类型,并且不一定如此,您的程序将被拒绝。

您可能认为您正在测试他们是否选择了Twothings类型,但没有!类型信息在运行时被删除,因此无法进行此类测试。

可以尝试恢复必要的运行时信息。首先,让我修复您的Leftthingleftthing之间的不一致。

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是依赖量词,表示隐藏类型参数未被删除,而是在运行时传递和匹配。那天还没到,但我认为它会。