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述語の引数へのアクセスを提供することになっているため、型がある場合Bool
k
X = arg1 -> arg2 -> ... -> argn -> Bool
それから
Arg X k = arg1 -> arg2 -> ... -> argn -> k
conjunction
これにより、2つの述語のすべての引数が収集される正しい結果タイプを構築できます。
この関数split
は、型の述語と型a
の続きを取り、型Bool -> r
の何かを生成しますArg a r
。の考え方は、最終的に述語から取得しsplit
たweをどうするかを知っていれば、その間に他のこと()を実行できるということです。Bool
r
当然のことながら、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つの引数を取ることができます。結果をフィードしてから呼び出すことができます。Arg
r
split
x
a
x
f
split
クラスを定義したら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
。