使用GADT

时间:2017-03-16 03:47:01

标签: haskell ghc

在下面的代码中,我试图匹配GADT构造函数Cons以使编译器看到xs非空:

{-# LANGUAGE DataKinds           #-} 
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-} 
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators       #-}

import Data.Typeable

data Foo (ts :: [*]) where
  Nil :: Foo '[]
  Cons :: (Typeable t) => Foo ts -> Foo ( t ': ts)

foo :: Foo xs -> IO ()
foo Nil = print "done"
foo (Cons rest :: Foo (y ': ys)) = do
  print $ show $ typeRep (Proxy::Proxy y)
  foo rest

不幸的是,这个简单的例子无法用GHC 8编译:

• Couldn't match type ‘xs’ with ‘y : ys’
  ‘xs’ is a rigid type variable bound by
    the type signature for:
      foo :: forall (xs :: [*]). Foo xs -> IO ()
  Expected type: Foo (y : ys)
    Actual type: Foo xs
• When checking that the pattern signature: Foo (y : ys)
    fits the type of its context: Foo xs
  In the pattern: Cons rest :: Foo (y : ys)
  In an equation for ‘foo’:
      foo (Cons rest :: Foo (y : ys))
        = print $ (show $ typeRep (Proxy :: Proxy y))

我知道类型推断对于GADT来说可能很棘手(例如#9695#10195#10338),但这是所以简单...

我需要做些什么来说服GHC当我在Cons上匹配时,GADT参数至少有一个元素?

1 个答案:

答案 0 :(得分:3)

您需要的只是从Proxy t

中提取Foo (t ': ts)的功能
fooFstType :: Foo (t ': ts) -> Proxy t 
fooFstType _ = Proxy 

请注意,由于Foo的类型参数为t ': ts,而不仅仅是ts,因此您可以引用表示类型签名中第一个元素的类型变量(而不是不知何故,身体与ScopedTypeVariables)。

您的功能变为

foo :: Foo xs -> IO ()
foo Nil = print "done"
foo f@(Cons rest) = do
  print $ show $ typeRep (fooFstType f)
  foo rest

另一种可能性是将工作转移到类型级别:

type family First (xs :: [k]) :: k where 
  First (x ': xs) = x 

foo :: forall xs . Foo xs -> IO ()
foo Nil = print "done"
foo (Cons rest) = do
  print $ show $ typeRep (Proxy :: Proxy (First xs))
  foo rest