1

パターン マッチングに型制約を設定できる拡張機能を知っている人はいますか? 例えば:

{Language Extension}
IsOrderable::(Eq a)=>a->IO Bool
IsOrderable x = case x of
    o::(Ord a=>a) -> do
                         putStrLn "This equatable thing is also orderable."
                         return True
    _             -> do
                         putStrLn "This equatable thing is not orderable."
                         return False

注:入力タイプに基づいて異なる反応をするモナドを作成できるように、これを求めています。具体的には、確率モナドを作ろうとしていたのですが、重複を結合できるように、入力の型が等価かどうかを確認したいと思います。

4

2 に答える 2

2

私はそのような拡張機能を知りません。さらに、GHC の現在のクラス実装方法でそのような拡張機能を作成するのは非常に難しいのではないかと思います。特に、GHC は、特定の型が特定のクラスを実装しているという「証拠」を渡すことによってクラスを実行します。foo :: Eq a => a -> IO Boolしたがって、実際に手段が2 つの引数をfoo取ることがわかります。この証拠は偽造できません。aEq a

ここで、提案されたコードで何が起こっているかを考えてみましょう: スコープ内に type の値とインスタンスaがあるという証拠がありEq aます。次に質問をします:このプログラムのどこかにインスタンスがあるという証拠はありOrd aますか? さらに、何が何であるかを前もって言うつもりはありませんa。とにかく、私の質問に対する正しい答えを教えてください、ありがとう!

これは、私が思いつくことができる最も標準的なトリッキーな例です。

{-# LANGUAGE ExistentialQuantification #-}
data Existential = forall a. Eq a => Existential a

tricky :: Existential -> IO Bool
tricky (Existential x) = isOrderable x

Existential型は別の型の値をラップします。ラップした値と、そのインスタンスに型が存在するといういくつかの証拠の 2 つだけを記憶しEqます。特に、型自体は覚えていません! さて、 を実行する時が来るとtricky、何らかの型の値 (どの型か忘れてしまいました) と、問題の型が のインスタンスであるという証拠が得られますEq。では、それが のインスタンスでもあるかどうかをどのように推測するのでしょうOrdか? 実際にはわかりません。どのインスタンスを探すべきかさえわかりません! (実際、妥当な方法で値を検査して、それがどのような型であるかを再構築しようとすることさえできません。できる検査は、Eq型クラスによって提供される操作だけです。それではあまりわかりません。Ord Int実例?それともOrd (Complex Double)インスタンス?わかりません。

一方、すでに次のようなことができます。

{-# LANGUAGE DefaultSignatures #-}
class Eq a => QueryOrd a where
    isOrd :: a -> IO Bool
    default isOrd :: Ord a => a -> IO Bool
    isOrd _ = putStrLn "yup" >> return True

isOrdAltDef :: Eq a => a -> IO Bool
isOrdAltDef _ = putStrLn "nope" >> return False

instance Eq a => QueryOrd (Complex a) where isOrd = isOrdAltDef
instance         QueryOrd MyFancyType where isOrd = isOrdAltDef

...しかし、 の非インスタンスQueryOrdごとに のインスタンスが 1 つ必要になります。OrdEq

于 2013-11-29T19:17:03.130 に答える
1

方法はありますが、きれいではありません。まず、型クラス インスタンスでディスパッチする関数を作成します。

class IsOrd a where
    isOrd :: a -> IO Bool

isOrdは最終的に 2 つのインスタンスを持ち、それぞれの に対応しますcase。もちろん、シンプルに

instance Ord a => IsOrd a where
    isOrd = putStrLn "Orderable!" >> return True
instance IsOrd a where
    isOrd = putStrLn "Not orderable" >> return False

インスタンス ヘッド ( IsOrd) が同じであるため機能しません。また、コンパイラの動作方法は、制約が保持されているかどうかに関係なくインスタンス ヘッドと一致し、その後でのみ制約をチェックするというものです。

ここから複雑な部分に入ります:ここここで説明されています。(2 番目のリンク、Oleg Kiselyov の Type Classes に関するコレクションには、関連する他の情報が含まれている可能性がありますが、それについてはまだ知らないため、ここには含めません。一般的には優れたリソースでもあります!)。その本質は次のようになります。

data HTrue
data HFalse
instance (Ord a) => IsOrd' HTrue a where
  isOrd' _ x = putStrLn "Orderable." >> return True
instance IsOrd' HFalse a where
  isOrd' _ x = putStrLn "Not orderable" >> return False

インスタンス ヘッドにタイプ レベルのブール値を追加して、例caseの s が個別のインスタンス ヘッドになるようにしました。かなりナイスなアイデア!

あなたが心配しなければならない他の詳細があり、その一部は完全には理解できませんが、とにかくそれを機能させるのは簡単です:ここにあなたが望むことをするいくつかのコードがあります (あなたはずっと Eq インスタンスをスレッド化する必要があります)制約もIsOrd必要な場合は、それ以降)。Eq最終結果は次のとおりです。

*Scratch> :l scratch.hs
[1 of 1] Compiling Scratch          ( scratch.hs, interpreted )
Ok, modules loaded: Scratch.
*Scratch> isOrd (5::Int)
Orderable.
True
*Scratch> isOrd ('c')
Not orderable
False
于 2014-02-10T14:32:20.010 に答える