所以,我终于找到了一个可以使用新DataKinds
扩展的任务(使用ghc 7.4.1)。这是我正在使用的Vec
:
data Nat = Z | S Nat deriving (Eq, Show)
data Vec :: Nat -> * -> * where
Nil :: Vec Z a
Cons :: a -> Vec n a -> Vec (S n) a
现在,为方便起见,我想实现fromList
。简单的递归/折叠基本上没有问题 - 但我无法弄清楚如何给它正确的类型。作为参考,这是Agda版本:
fromList : ∀ {a} {A : Set a} → (xs : List A) → Vec A (List.length xs)
我的Haskell方法,使用我看到here的语法:
fromList :: (ls :: [a]) -> Vec (length ls) a
fromList [] = Nil
fromList (x:xs) = Cons x (fromList xs)
这给了我一个parse error on input 'a'
。我发现的语法是否正确,或者他们改变了吗?我还在链接的代码中添加了一些更多的扩展,这些扩展也没有帮助(目前我有GADTs, DataKinds, KindSignatures, TypeOperators, TypeFamilies, UndecidableInstances
)。
我的另一个怀疑是我无法绑定多态类型,但我对此进行了测试:
bla :: (n :: Nat) -> a -> Vec (S n) a
bla = undefined
也失败了Kind mis-match Expected kind 'ArgKind', but 'n' has kind 'Nat'
(不知道这意味着什么)。
任何人都可以帮助我使用fromList
的工作版本并澄清其他问题吗?不幸的是,DataKinds
还没有很好地记录,并且似乎假设每个使用它的人都具有深刻的类型理论知识。
答案 0 :(得分:10)
与Agda不同,Haskell没有依赖类型,因此无法完全按照您的意愿行事。类型不能通过值进行参数化,因为Haskell在运行时和编译时之间强制执行阶段区分。 DataKinds
在概念上的工作方式实际上非常简单:数据类型提升到种类(类型),数据构造函数提升为类型。
fromList :: (ls :: [a]) -> Vec (length ls) a
有几个问题:(ls :: [a])
没有任何意义(至少当你只使用促销伪造依赖类型时),length
是一个类型变量而不是类型函数。你想说的是
fromList :: [a] -> Vec ??? a
其中???
是列表的长度。问题是你无法在编译时得到列表的长度......所以我们可能会尝试
fromList :: [a] -> Vec len a
但这是错误的,因为它说fromList
可以返回任何长度的列表。相反,我们想说的是
fromList :: exists len. [a] -> Vec len a
但是Haskell不支持这个。代替
data VecAnyLength a where
VecAnyLength :: Vec len a -> VecAnyLength a
cons a (VecAnyLength v) = VecAnyLength (Cons a v)
fromList :: [a] -> VecAnyLength a
fromList [] = VecAnyLength Nil
fromList (x:xs) = cons x (fromList xs)
您实际上可以通过模式匹配使用VecAnyLength
,从而获得(本地)伪随机类型的值。
同样,
bla :: (n :: Nat) -> a -> Vec (S n) a
不起作用,因为Haskell函数只能接受类*
的参数。相反,你可以尝试
data HNat :: Nat -> * where
Zero :: HNat Z
Succ :: HNat n -> HNat (S n)
bla :: HNat n -> a -> Ven (S n) a
甚至可以定义
bla Zero a = Cons a Nil
bla (Succ n) a = Cons a (bla n a)
答案 1 :(得分:4)
你可以在这里使用一些类型类魔术(更多信息见HList):
{-# LANGUAGE GADTs, KindSignatures, DataKinds, FlexibleInstances
, NoMonomorphismRestriction, FlexibleContexts #-}
data Nat = Z | S Nat deriving (Eq, Show)
data Vec :: Nat -> * -> * where
Nil :: Vec Z a
Cons :: a -> Vec n a -> Vec (S n) a
instance Show (Vec Z a) where
show Nil = "."
instance (Show a, Show (Vec m a)) => Show (Vec (S m) a) where
show (Cons x xs) = show x ++ " " ++ show xs
class FromList m where
fromList :: [a] -> Vec m a
instance FromList Z where
fromList [] = Nil
instance FromList n => FromList (S n) where
fromList (x:xs) = Cons x $ fromList xs
t5 = fromList [1, 2, 3, 4, 5]
但这并不能解决问题:
> :t t5
t5 :: (Num a, FromList m) => Vec m a
列表是在运行时形成的,它们的长度在编译时是未知的,因此编译器无法推断t5
的类型,必须明确指定:
*Main> t5
<interactive>:99:1:
Ambiguous type variable `m0' in the constraint:
(FromList m0) arising from a use of `t5'
Probable fix: add a type signature that fixes these type variable(s)
In the expression: t5
In an equation for `it': it = t5
*Main> t5 :: Vec 'Z Int
*** Exception: /tmp/d.hs:20:3-19: Non-exhaustive patterns in function fromList
*Main> t5 :: Vec ('S ('S ('S 'Z))) Int
1 2 3 *** Exception: /tmp/d.hs:20:3-19: Non-exhaustive patterns in function fromList
*Main> t5 :: Vec ('S ('S ('S ('S ('S 'Z))))) Int
1 2 3 4 5 .
*Main> t5 :: Vec ('S ('S ('S ('S ('S ('S ('S 'Z))))))) Int
1 2 3 4 5 *** Exception: /tmp/d.hs:23:3-40: Non-exhaustive patterns in function fromList
具有依赖类型的语言具有从术语到类型的映射,类型也可以在运行时动态形成,因此不存在此问题。
答案 2 :(得分:0)
在先前的答案之上:
值级别,从[a]
到exist n. Vec n a
值到键入的值,从[a]
到Vec 5 a
,其中您必须提供特定的n
。
第一个转换的变体,就像
reify :: [a] -> (forall (n::Nat). Proxy n -> Vec n a -> w) -> w
reify [] k = k (Proxy @ 'Z) Nil
reify (x:xs) k = reify xs (\(_ :: Proxy n) v -> k (Proxy @ ('S n)) (Cons x v))
它仍然从值[a]
变为其中Vec n a
被(静态)量化的类型化值n
。这类似于VecAnyLength
方法,但没有引入实际的数据类型来进行量化。
此处的proxy
是将n
明确表示为Nat
。可以将其从代码中删除,并使n
保持沉默,仅以类型Vec n a
出现,而不像Proxy @ ('S n)
那样提供给构造的值。