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

これらはこの質問で対処された問題と同じだと思いますが、ここでその解決策を適応させるのに問題があります。型ファミリまたはその他の方法を使用して、再帰的な継続スタックに対するより使いやすいソリューションを考え出すことはできますか?

4

2 に答える 2

7

リンクされた質問への回答には、次の非常に便利なトリックが隠されています。インスタンス ヘッドを一般化し、インスタンス コンテキストを特殊化します。

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

使用するインスタンスを選択するとき、GHC は使用可能なインスタンス ヘッドのみを検査し (コンテキストは調べません)、タイプについて現在わかっているものと一致するインスタンス (存在する場合) を選択します。この選択を行う前に型を特殊化することはありません。特殊化によって 1 つ以上の使用可能なインスタンス ヘッドが一致する場合でもです。したがって、ここで与えられたインスタンスと上記の質問のインスタンスの違いは、これがより一般的であるということです。これは中間タイプが である場合に常に適用されますがTop、あなたのものは中間タイプが でTop あり、他の 2 つについて十分にわかっている場合にのみ適用されます。タイプが等しいことを認識します。

あなたのものは他の潜在的なインスタンスと重複することは少なくなりますが、これにより推論エンジンがより強力に促進されます。

于 2012-05-27T21:58:28.813 に答える
6

クリーネ閉包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

しかし、型クラスのアプローチが必要な場合、私は干渉しません。

于 2012-05-27T22:05:55.970 に答える