2012-10-03 6 views
7

今日私は型クラスを使って、入力として任意の型の任意の組み合わせを取って、同じ型の他の述語を返したが基本的な操作をいくつか適用して、任意の型の述語の関数を誘導的に構築しました。例えばハスケルではどのようにm-ary述語とn-ary述語をとり、(m + n)-ary述語を構築できますか?

conjunction (>2) even 

は、2と

conjunction (>=) (<=) 

は=

すべてが順調と良い返されるよりも、より大きな偶数のために真と評価述語を返すその部分の作業を得たが、それでしょう2つの述語の結合をそれぞれの結合述語の各入力に対して1つの入力をとる述語として定義したいのであれば、どうしたらよいでしょうか?たとえば、

:t conjunction (>) not 

は、Ord a => a - > a - > Bool - > Boolを返します。これはできますか?もしそうなら、どうですか?

答えて

6

このソリューションにはTypeFamiliesが必要です。

{-# LANGUAGE TypeFamilies #-} 

アイデアはn項述語のクラスPredを定義することです:

class Pred a where 
    type Arg a k :: * 
    split :: a -> (Bool -> r) -> Arg a r 

問題は、すべての述語への再シャッフル引数についてですので、これはクラスが行うことを目的とするものです。関連するタイプArgkで最終Boolを置き換えることにより、n項述語の引数へのアクセス権を与えることになっているので、我々はタイプそして

X = arg1 -> arg2 -> ... -> argn -> Bool 

Arg X k = arg1 -> arg2 -> ... -> argn -> k 

を持っている場合、これができるようになります我々はconjunctionの正しい結果型を構築し、2つの述語のすべての引数を収集することにしました。

機能splitはタイプaの述語と種類Bool -> rの継続を取り、タイプArg a rの何かを生成します。 splitというアイデアは、最後に述語から取得するBoolをどうすればよいか分かっていれば、その間に他のもの(r)を実行できます。

驚くほど、我々は2つのインスタンス、Bool用とターゲットがすでに述語される機能のための1つをする必要がありますない:

instance Pred Bool where 
    type Arg Bool k = k 
    split b k = k b 

Boolは引数を持っていないので、Arg Bool kは単にkを返します。また、splitについては、Boolが既にありますので、すぐに継続を適用できます。私たちはタイプa -> rの述語を持っている場合

instance Pred r => Pred (a -> r) where 
    type Arg (a -> r) k = a -> Arg r k 
    split f k x = split (f x) k 

、そしてArg (a -> r) ka ->で始まる必要があり、我々はrに再帰的にArgを呼び出すことで継続します。 splitについては、xaの3つの引数を取ることができるようになりました。 xからfにフィードし、結果としてsplitを呼び出すことができます。

我々はconjunctionを定義することは容易である、Predクラスを定義したら:

conjunction :: (Pred a, Pred b) => a -> b -> Arg a (Arg b Bool) 
conjunction x y = split x (\ xb -> split y (\ yb -> xb && yb)) 

関数は二つの述語を取り、タイプ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のような)他の関数を書くことが些細であること。

4
{-# LANGUAGE TypeFamilies #-} 

class RightConjunct b where 
    rconj :: Bool -> b -> b 

instance RightConjunct Bool where 
    rconj = (&&) 

instance RightConjunct b => RightConjunct (c -> b) where 
    rconj b f = \x -> b `rconj` f x 

class LeftConjunct a where 
    type Ret a b 
    lconj :: RightConjunct b => a -> b -> Ret a b 

instance LeftConjunct Bool where 
    type Ret Bool b = b 
    lconj = rconj 

instance LeftConjunct a => LeftConjunct (c -> a) where 
    type Ret (c -> a) b = c -> Ret a b 
    lconj f y = \x -> f x `lconj` y 

conjunction :: (LeftConjunct a, RightConjunct b) => a -> b -> Ret a b 
conjunction = lconj 

希望ではありませんが、それ以外の場合はお気軽に質問してください。

また、2つのクラスを1つにマージすることもできますが、2つのクラスによってアイデアがより明確になると感じています。