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