2011-07-20 9 views
6

私はモナドとしてコンテナMを持つモナドインスタンスを定義したいと思います。aというタイプは、Showのメンバーでなければなりません。この制約(aはメンバであるShowです)は、型システムによって保証される必要があります。Typeclass ShowでMonadインスタンス "m a"と "a"を定義するには?

私はこのようにしてみてください、しかしMは、右の種類の、残念ながらできませんそれを与えた:

data M = forall a. Show a => M a 

instance Monad M where 
return x = M x 

それを達成するために、他のすべての試み、次のような問題に遭遇:Monadはコンストラクタクラスですので、I含まれている要素のタイプaに明示的にアクセスできないため、制限できません。

新しいMonadクラスを定義することなく誰でもこの解決策を知っていますか?

答えて

7

まあ、GADTsを使用して、いくつかの意味での型コンストラクタのパラメータを制限することが実際に可能である:

data M a where 
    M :: (Show a) => a -> M a 

残念ながら、これは実際にここであなたを助けていません。実際には状況が悪化します。なぜなら、制約なしでMonadインスタンスを持つよりも、インスタンスをまったく書き込めなくなるからです。

上記のコンストラクタタイプのシグネチャを見ると、それは明らかにreturnに似ています。これはあなたがしていることが基本的に不可能であることを示しています。戻り値の型は(Monad m) => a -> m aであり、常にバインドされていない型変数は最外層で暗黙的に全面的に数値化されているので、 "可能なすべての型の場合と可能なすべての型mMonadの値で与えられますタイプaの場合は、タイプm aの値を作成することができます。 "for all"という言い回しはかなりリテラルです。戻り値の型は型変数を使用するだけではなく、どんな型でも許されることを積極的に宣言しています。

だから、要するに、いいえ。標準Monadクラスには明示的に反対のが指定されているため、必要な処理を行う方法はありません。

2

いいえ、説明は簡単ですが、これは不可能です。 returnの型シグネチャを見て:

return :: Monad m => a -> m a 

あなたのモナドは、任意のコンテンツタイプを受け入れなければならないので、署名は、変更することはできません。型クラス自体にはコンテンツタイプは記述されていないため、制約を強制する方法はありません。

あなたができることの1つは、それを必要とするすべての関数にこの制約を記述し、グローバル制約を取り除くことです。 Show制約の存在を証明することができれば、システムの健全性は保証されます。

+0

'カインドミスマッチ: 'Monad'の最初の引数はkind '* - > *'である必要がありますが、 'M a'はkind '*' ' –

+0

@camccannです。いいえ、私はその質問を理解していない。あなたのものに似た私の更新された答えを見てください。 – fuz

+0

ダウンボッターは自分自身を説明できますか? – fuz

1

これはいくつかの極端なトリッキーで可能です。実装についてはrmonadパッケージをご覧ください。しかし、おそらくその価値はありません。

なぜaShowであるという制約が必要なのですか?その時点でShow制約を適用するほうが簡単になり、必要に応じて自然に拡張されます。

+0

質問は "新しいMonadクラスを定義せずに"指定しました。いろいろな近似を得ることが可能なトリッキーな程度がありますが、標準的な 'Monad'を囲むことはできません。 'RMonad'はおそらく、' AsMonad'ラッパーのためにできるだけ少ないコンセッションを可能にする点で最も近いでしょう。 –

+0

@camccann: 'AsMonad'は、標準の' Monad'クラスで使用できます。 –

+0

はい、しかし(明らかに)そうすることは 'RMonad'の制限を無視します。 'AsMonad'だけを使っても、あなたは普通の' Monad'インスタンスで直接行うことはできません。これの有用性は、ラッピング/アンラッピングが余分な情報を格納することで、通常の 'Monad'を使って関数をラウンドトリップするときに、できるだけ多くの' RMonad'プロパティを保存できるようにすることです。 –

5

あなたが求めていることを正確に行うことはできないかもしれませんが、特定のモナドがShowを使ってやっていることを明示的に行うアクションを提供する可能性もあります。それはあなたが持っていると仮定、次のとおりです。

data M a = {- ... -} 
instance Monad M where -- notice: no Show constraint 
    {- ... -} 

次に、あなたは、さらにいくつかのアクションを供給できます。

report :: Show a => M a -> M a 

私はShowと、このパターンの良い使用の私の頭の上から考えることはできません、しかし私は、あなたがOrdの制約を望むかもしれない同様の例を知っています。設定は、非確定的(例えば、[a])のモナドを作成したいが、重複はありません(Set aなど)。重複を削除するには、EqまたはOrdのようなコンテキストが必要ですが、return/>>=の操作ごとに要求することはできません。

newtype Setlike a = Setlike { toList :: [a] } 
instance Monad Setlike where 
    return x = Setlike [x] 
    Setlike xs >>= f = [y | x <- xs, let Setlike ys = f x, y <- ys] 

collapse :: Ord a => Setlike a -> Setlike a 
collapse = Setlike . Data.Set.toList . Data.Set.fromList . toList 

これがそうのように使用することができます:いくつかのv1のペアリングとv2はに起こる場合でも、その後

valuesOfInterest = collapse $ do 
    v1 <- allValues 
    v2 <- allValues 
    doSomethingInteresting v1 v2 

だからではなく、私たちは、ユーザーが明示的に重複が合体しなければならないポイントをマークすることを要求します同じ関心の値になり、その値は結果に1回しか表示されません。

あなたのユースケースでも同様のトリックが可能です。

関連する問題