提升类型的模式匹配

时间:2013-02-13 08:06:58

标签: haskell pattern-matching higher-kinded-types type-kinds

我们可以为类*的参数编写单个,全面的实例:

class MyClass d where
    f :: d -> Int

instance MyClass (Maybe d) where  
    f _ = 3

test1 :: Maybe d -> Int
test1 x = f x

编译得很好,请注意我们在test1上不需要(MyClass(Maybe d))约束,因为编译器会找到任何Maybe d的匹配实例。

我们甚至可以为类型构造函数的类参数编写一个包罗万象的实例:

class MyClass2 (a :: * -> *) where
    g :: Tagged (a Int) Int

instance MyClass2 a where
    g = Tagged 3

test2 :: Int
test2 = untag (g :: Tagged (Maybe Int) Int)

这也编译得很好,而test2不需要(MyClass2 Maybe)约束,因为编译器找到了匹配的实例。

考虑以下玩具代码来计算(提升的)类型列表的长度:

{-# LANGUAGE TypeOperators,
DataKinds,
KindSignatures,
GADTs,
FlexibleInstances,
FlexibleContexts,
ScopedTypeVariables #-}

import Data.Tagged
import Data.Proxy

data HList :: [*] -> * where
              HNil :: HList '[]
              HCons :: a -> HList as -> HList (a ': as)

class ListLen a where
    len :: Tagged a Int

instance ListLen (HList '[]) where
    len = Tagged 0

instance (ListLen (HList as)) => ListLen (HList (a ': as)) where
    len = Tagged (1+(untag (len :: Tagged (HList as) Int)))

test3 :: Int
test3 = untag (len :: Tagged (HList '[Int, Double, Integer]) Int)

test4 :: (Proxy (HList qs)) -> Int
test4 (_ :: Proxy (HList qs)) = untag (len :: Tagged (HList qs) Int) -- error occurs here

这会导致错误:

No instance for (ListLen (HList qs))
      arising from a use of `len'
    Possible fix: add an instance declaration for (ListLen (HList qs))
    ...

如果我们注释掉test4的签名,GHCi会将类型推断为

test4 :: (ListLen (HList qs)) => (Proxy (HList qs)) -> Int

但这不是必需的。显然,我已经为ListLen编写了与任何可以想象的类型列表匹配的实例:“空列表”案例和“缺点”案例。如果我将ListLen参数的类型更改为具有[*]种类(即删除HList及其实例中的ListLen包装器),则问题仍然存在。

如果我们注释掉test4test3编译并运行正常:因为我(基本上)为我如何构造类型列表给出了显式语法('[Int,Double,Integer]),编译器是能够找到匹配的实例。

我正在尝试编写为我构建类型列表的代码,因此我不必写出显式类型列表语法。但是,似乎使用显式语法是GHC能够匹配这些综合实例的唯一方法。也许有一个我失踪的案子?语法我没用?

我能做些什么才能让GHC意识到我有一个类型为[*]的实例? 我正在使用GHC 7.4.2。此可能与解析非推广类型的previous post相关。可以找到关于类型推广的 论文here

1 个答案:

答案 0 :(得分:2)

这不是你想要的,但是非常接近。我从

开始
data Proxy a where
  Proxy :: ListLen a => Proxy

然后

data Proxy a where
  Proxy :: Tagged a Int -> Proxy a

导致

test4 :: (Proxy (HList qs)) -> Int
test4 (Proxy len) = untag len

问题是您需要具有约束,或者Proxy包含所需类的证据(即使所有可用类型都存在实例)。这里,类方法只包含在Proxy

完全不同的选择是根本不使用类型类,并以通常的方式实现len(从Data.List.length转录)。

len :: HList a -> Int
len l = len' l 0
  where
    len' :: HList a -> Int -> Int
    len' HNil a = a
    len' (HCons _ xs) a = len' xs $! a + 1