我在Haskell和TypeFamilies等玩。我正在尝试创建一个简单的示例,在该示例中,我下面有一个采用某种类型(Parsers ty
)的函数,以及一个采用许多依赖于ty
的参数并输出一些结果的函数。
这是前面的整个代码片段:
{-# LANGUAGE UndecidableInstances, TypeFamilies,GADTs #-}
data V a --"variable"
data S a --"static"
data C a b --combination
--define data type:
data Parsers ty where
ParVar :: a -> Parsers (V a)
ParStatic :: a -> Parsers (S a)
ParCombine :: Parsers ty1 -> Parsers ty2 -> Parsers (C ty1 ty2)
--basic type alias to below, output set to Bool
type ExtractVars parsers = GetVars parsers Bool
--we want to convert some combination of C/S/V
--types into a function taking, from left to right,
--each type inside S as parameter
type family GetVars parsers output where
GetVars (S a) output = output
GetVars (V a) output = a -> output
GetVars (C a b) output = GetVars a (GetVars b output)
--this function uses the above such that, hopefully,
--the second argument to be provided will be a function
--that will take each S type as an argument and return
--a bool. We then return that bool from the whole thing
getVars :: Parsers p -> ExtractVars p -> Bool
getVars (ParVar s) fn = fn s
getVars (ParStatic _) fn = fn
-- this is the offending line:
getVars (ParCombine a b) fn = getVars b (getVars a fn)
我的
Parsers ty
类型似乎做对了;用它构造事物会导致我期望的类型,例如:ParVar "hi"
:: Parsers (V [Char])
ParCombine (ParVar "hi") (ParStatic "woop")
:: Parsers (C (V [Char]) (S [Char]))
ParCombine (ParVar 'h') (ParCombine (ParStatic True) (ParVar "hello"))
:: Parsers (C (V Char) (C (S Bool) (V [Char])))
同样,我的类型族
GetVars
似乎做正确的事情,执行如下转换:(C (V a) (V b)) out => a -> b -> out
(V a) out => a -> out
(S a) out => out
(C (S a) (V b) out => b -> out
基本上,以正确的顺序正确地选择用
V
标记的内容,并忽略S
然后,我的
getVars
函数在其类型签名中使用它:getVars :: Parsers p -> ExtractVars p -> Bool
要说什么
p
是什么,需要一个与我的类型别名ExtractVars p
匹配的函数,然后返回Bool
。在我的
getVars
函数中,这两个定义没有问题:getVars (ParVar s) fn = fn s
getVars (ParStatic _) fn = fn
但最后一个:
getVars (ParCombine a b) fn = getVars b (getVars a fn)
无法进行类型检查,并显示以下错误:
test.hs:74:42:
Could not deduce (GetVars ty2 Bool ~ Bool)
from the context (p ~ C ty1 ty2)
bound by a pattern with constructor
ParCombine :: forall ty1 ty2.
Parsers ty1 -> Parsers ty2 -> Parsers (C ty1 ty2),
in an equation for ‘getVars’
at test.hs:74:10-23
Expected type: ExtractVars ty2
Actual type: Bool
Relevant bindings include b :: Parsers ty2 (bound at test.hs:74:23)
In the second argument of ‘getVars’, namely ‘(getVars a fn)’
In the expression: getVars b (getVars a fn)
test.hs:74:52:
Could not deduce (GetVars ty1 Bool
~ GetVars ty1 (GetVars ty2 Bool))
from the context (p ~ C ty1 ty2)
bound by a pattern with constructor
ParCombine :: forall ty1 ty2.
Parsers ty1 -> Parsers ty2 -> Parsers (C ty1 ty2),
in an equation for ‘getVars’
at test.hs:74:10-23
NB: ‘GetVars’ is a type function, and may not be injective
Expected type: ExtractVars ty1
Actual type: ExtractVars p
Relevant bindings include
b :: Parsers ty2 (bound at test.hs:74:23)
a :: Parsers ty1 (bound at test.hs:74:21)
In the second argument of ‘getVars’, namely ‘fn’
In the second argument of ‘getVars’, namely ‘(getVars a fn)’
Failed, modules loaded: none.
(第74行是getVars失败的ParCombine定义)
我一直把头撞在桌子上,试图弄清楚哪里出了问题,但无济于事,真的想牢牢抓住类型家庭的来龙去脉,因为它们看起来很棒(例如,上面的例子-能够根据输入期望某些功能类型-太酷了!)
谁能指出我的正确方向或纠正我可能非常愚蠢的错误?
谢谢!
(PS我正在使用GHC 7.10.1并在ghci中运行它)
编辑只是说这个例子是出于有关类型族的论文的启发,特别是sprintf例子here。也许我在尝试从中提取想要的东西时已经跳过了一些重要的事情!
最佳答案
只是概括getVars
:
getVars :: Parsers p -> GetVars p out -> out
getVars (ParVar s) fn = fn s
getVars (ParStatic _) fn = fn
getVars (ParCombine a b) fn = getVars b (getVars a fn)
原始类型为
getVars a fn :: Bool
,但我们需要getVars a fn :: ExtractVars ty2
。通常,如果我们想对GADT进行大量计算,则必须确保我们具有足够通用的类型,这样我们才能进行递归调用(因为在递归时类型或类型索引可能会发生变化)。附带说明一下,代表GADT索引的惯用方式是使用
DataKinds
和提升类型:{-# LANGUAGE DataKinds #-}
data ParserTy a = V a | S a | C (ParserTy a) (ParserTy a)
data Parsers (ty :: ParserTy *) where
ParVar :: a -> Parsers (V a)
ParStatic :: a -> Parsers (S a)
ParCombine :: Parsers ty1 -> Parsers ty2 -> Parsers (C ty1 ty2)
其余代码保持不变。这样,我们就不会用真正不属于该类型的任意类型污染
ParserTy
,并且可以在其上编写详尽的类型族。