2012-05-27 53 views
3

我的工作,我希望定义我在这里已经简化为一个递归类库:类型线程异构列表和缺省(?)类型族?

{-# LANGUAGE MultiParamTypeClasses 
      , FlexibleInstances #-} 

data Snoc st b c = Snoc (st b) (c -> b) 
data Top a = Top 

class StackTo a st c where 
    runStack :: st c -> (c -> a) 
instance StackTo a Top a where 
    runStack _ = id 
instance (StackTo a st b) => StackTo a (Snoc st b) c where 
    runStack (Snoc st' cb) = runStack st' . cb 

这让我做的,例如

*Main Data.Label> let f = runStack $ Snoc (Snoc Top fst) head :: [(a,x)] -> a 
*Main Data.Label> f [('a',undefined)] 
'a' 

但这似乎需要谨慎使用类型注释,否则......

*Main Data.Label> let f = runStack $ Snoc (Snoc Top fst) head 

<interactive>:1:1: 
    No instance for (StackTo a0 Top b0) 
     arising from a use of `runStack' 
    Possible fix: add an instance declaration for (StackTo a0 Top b0) 
    In the expression: runStack 
    In the expression: runStack $ Snoc (Snoc Top fst) head 
    In an equation for `it': it = runStack $ Snoc (Snoc Top fst) head 

我觉得这些都是在this question解决同样的问题,但是我无法在这里适应这种解决方案。我可以使用类型族还是其他方法为我的递归延续堆栈提供更加用户友好的解决方案?

+1

你可能想要考虑一个'class Stack st',其中包含像'type Domain st a'和'type Codomain st a'这样的关联类型'或者类似的东西,但是我还没有仔细阅读以便知道这个会为你工作。 –

回答

7

链接问题的答案隐藏了以下相当有用的技巧:概括实例头,并专注于实例上下文。

instance a ~ b => StackTo a Top b where 
    runStack _ = id 

当选择一个实例来使用,GHC检查可用实例头只 - 不是上下文 - 并挑选一个(如果有的话)匹配什么是目前已知的关于类型。在进行此选择之前,它不会专门化一种类型,即使专门化可以允许一个或多个可用实例头匹配。因此,这里给出的例子和上面问题中的例子之间的区别在于,这个例子更一般:只要中间类型为Top,而这个例子只适用于中间类型为Top,我们就知道了关于另外两种类型要知道他们是平等的。

您将与更少的其他潜在实例重叠,但这会更强烈地鼓励推理引擎。

+0

太好了,谢谢!你能澄清'实例堆栈顶部a'和'实例a〜b =>堆栈顶部b'之间的区别吗?前者更多是*约束*而后者是平等的*断言*? (文档似乎并不表明,所以我可能关闭) – jberryman

+1

@jberryman我已经给答案增加了一些解释。 –

6

是否有某些特殊原因为什么Kleene明星 GADT不会做这项工作?

data Star r a b where 
    Nil :: Star r a a 
    Cons :: r a b -> Star r b c -> Star r a c 

compose :: Star (->) a b -> a -> b 
compose Nil   = id 
compose (Cons f fs) = compose fs . f 

但是,如果你需要一个类的类方法,我不会干预。

+1

我实际上正在做一个基于thrist写的lib的变体,这就是这个确切的构造(虽然我不知道Kleene的明星,谢谢!),所以我正在公开中间类型。 – jberryman