7

今日、私は型クラスを使用して、任意の型の任意の組み合わせを入力として受け取り、同じ型の他の述語を返すが、いくつかの基本的な操作を適用した、任意のアリティの述語の関数を帰納的に構築することで遊んだ。例えば

conjunction (>2) even

2より大きい偶数に対してtrueと評価される述語を返します。

conjunction (>=) (<=)

=を返します

すべてうまくいきましたが、その部分は機能しましたが、2つの述語の接続詞を、結合された各述語の入力ごとに1つの入力を受け取る述語として定義したい場合はどうでしょうか。例えば:

:t conjunction (>) not

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

4

2 に答える 2

6

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

于 2012-10-03T21:14:42.530 に答える
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つのクラスによってアイデアがより明確になると思います。

于 2012-10-03T20:51:14.303 に答える