31

次のようなタイプの関数がある場合

f :: (Ord a) => a -> a -> Bool
f a b = a > b

この関数をnotでラップするmake関数が欲しいです。

たとえば、このような関数を作る

g :: (Ord a) => a -> a -> Bool
g a b = not $ f a b

コンビネータを次のように作成できます

n f = (\a -> \b -> not $ f a b)

しかし、方法がわかりません。

*Main> let n f = (\a -> \b -> not $ f a b)
n :: (t -> t1 -> Bool) -> t -> t1 -> Bool
Main> :t n f
n f :: (Ord t) => t -> t -> Bool
*Main> let g = n f
g :: () -> () -> Bool

私は何を間違っていますか?

そして、ボーナスの質問は、より多くのパラメーターと最も少ないパラメーターを使用して関数に対してこれを行う方法です。

t -> Bool
t -> t1 -> Bool
t -> t1 -> t2 -> Bool
t -> t1 -> t2 -> t3 -> Bool
4

4 に答える 4

42

実際、型クラスで任意のアリティを実行するのは信じられないほど簡単です。

module Pred where

class Predicate a where
  complement :: a -> a

instance Predicate Bool where
  complement = not

instance (Predicate b) => Predicate (a -> b) where
  complement f = \a -> complement (f a)  
  -- if you want to be mysterious, then
  -- complement = (complement .)
  -- also works

ge :: Ord a => a -> a -> Bool
ge = complement (<)

このクールな問題を指摘してくれてありがとう。ハスケルが大好きです。

于 2009-01-08T00:52:22.423 に答える
41

思考実験と概念実証のために残したほうがよい型クラスをハッキングしたい場合を除き、複数の引数に一般化することはできません。しようとしないでください。

主な質問に関しては、これは Conal Elliott のセマンティック エディター コンビネーターで最もエレガントに解決されます。セマンティック エディター コンビネーターは、次のような型の関数です。

(a -> b) -> F(a) -> F(b)

F(x)を含む式はどこにありますかx。代わりに a を取る「反変」エディタ コンビネータもあります(b -> a)。直観的には、エディター コンビネーターは、より大きな値の一部を選択して操作します。必要なものは次のresultとおりです。

result = (.)

操作しようとしている式のタイプを見てください。

a -> a -> Bool

この型の結果 (コドメイン) はであり、そのa -> Boolの結果は であり、それを適用しようとしています。したがって、関数の結果の結果に適用するには、次のように記述します。Boolnotnotf

(result.result) not f

これは見事に一般化されています。さらにいくつかのコンビネータを次に示します。

argument = flip (.)     -- contravariant

first f (a,b) = (f a, b)
second f (a,b) = (a, f b)

left f (Left x) = Left (f x)
left f (Right x) = Right x
...

したがって、型の値がある場合x:

Int -> Either (String -> (Int, Bool)) [Int]

そして、Bool に適用したい場合notは、そこに到達するためのパスを綴るだけです。

(result.left.result.second) not x

ああ、まだ Functors を理解していれば、それfmapがエディタ コンビネータであることに気付くでしょう。実際、上記は次のように綴ることができます。

(fmap.left.fmap.fmap) not x

しかし、拡張された名前を使用する方が明確だと思います。

楽しみ。

于 2009-01-06T01:54:17.697 に答える
12

n コンビネータは次のように記述できます。

n = ((not .) .)

おまけの質問に関しては、一般的な方法は、これらのいくつかを作成することです。

lift2 = (.).(.)
lift3 = (.).(.).(.)
lift4 = (.).(.).(.).(.)
lift5 = (.).(.).(.).(.).(.)

于 2009-01-05T18:33:56.713 に答える
9

Re:何が悪いの?:

あなたのコンビネータは問題ないと思いますが、最上位で let-bind すると、Haskell の厄介な「デフォルト ルール」の 1 つが作用し、バインディングが一般化されません。

Prelude> :ty (n f)
(n f) :: (Ord t) => t -> t -> Bool
Prelude> let g = n f
Prelude> :ty g
g :: () -> () -> Bool

型クラスに適用される「モノモーフィズムの制限」に悩まされているかもしれません。いずれにせよ、トップレベルのループから抜け出して、明示的な型シグネチャを持つ別のファイルに何かを入れると、すべて正常に機能します。

module X where

n f = (\a -> \b -> not $ f a b)
f a b = a > b

g :: Ord a => a -> a -> Bool
g = n f

おまけの質問: より多くの型パラメーターでこれを行うには、型クラス システムで壊滅的なトリックを試すことができます。参照すべき 2 つの論文は、QuickCheck に関するHughes と Claessen の論文、および Ralf Hinze の論文Generics for the Massesです。

于 2009-01-05T19:09:04.927 に答える