TypeFamiliesこのソリューションが必要になります。
{-# LANGUAGE TypeFamilies #-}
Predアイデアは、n-ary述語のクラスを定義することです。
class Pred a where
type Arg a k :: *
split :: a -> (Bool -> r) -> Arg a r
問題はすべて述語への引数を再シャッフルすることであるため、これがクラスの目的です。関連付けられた型は、finalをにArg置き換えることにより、n-ary述語の引数へのアクセスを提供することになっているため、型がある場合Boolk
X = arg1 -> arg2 -> ... -> argn -> Bool
それから
Arg X k = arg1 -> arg2 -> ... -> argn -> k
conjunctionこれにより、2つの述語のすべての引数が収集される正しい結果タイプを構築できます。
この関数splitは、型の述語と型aの続きを取り、型Bool -> rの何かを生成しますArg a r。の考え方は、最終的に述語から取得しsplitたweをどうするかを知っていれば、その間に他のこと()を実行できるということです。Boolr
当然のことながら、2つのインスタンスが必要になります。1Boolつはターゲットがすでに述語である関数用ともう1つです。
instance Pred Bool where
type Arg Bool k = k
split b k = k b
ABoolには引数がないため、Arg Bool k単に。を返しますk。また、についてsplitは、Boolすでに持っているので、すぐに継続を適用できます。
instance Pred r => Pred (a -> r) where
type Arg (a -> r) k = a -> Arg r k
split f k x = split (f x) k
タイプの述語がある場合は、でa -> r開始Arg (a -> r) kする必要があり、を再帰的にa ->呼び出すことで続行します。の場合、タイプがであるという3つの引数を取ることができます。結果をフィードしてから呼び出すことができます。Argrsplitxaxfsplit
クラスを定義したらPred、簡単に定義できますconjunction。
conjunction :: (Pred a, Pred b) => a -> b -> Arg a (Arg b Bool)
conjunction x y = split x (\ xb -> split y (\ yb -> xb && yb))
この関数は2つの述語を取り、タイプの何かを返しますArg a (Arg b Bool)。例を見てみましょう:
> :t conjunction (>) not
conjunction (>) not
:: Ord a => Arg (a -> a -> Bool) (Arg (Bool -> Bool) Bool)
GHCiはこのタイプを拡張しませんが、拡張できます。タイプはと同等です
Ord a => a -> a -> Bool -> Bool
それはまさに私たちが望んでいることです。いくつかの例もテストできます。
> conjunction (>) not 4 2 False
True
> conjunction (>) not 4 2 True
False
> conjunction (>) not 2 2 False
False
このクラスを使用すると、Pred他の関数(など)を作成するのも簡単であることに注意してくださいdisjunction。