タイプ(C→D)→(A→B→C)のHaskell関数組成演算子→(A→B→D)
-
26-10-2019 - |
質問
通常の関数構成はタイプです
(.) :: (b -> c) -> (a -> b) -> a -> c
これは次のようなタイプに一般化する必要があると思います。
(.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
具体的な例:違いを計算します。書くことができます diffsq a b = (a - b) ^ 2
, 、 しかし みたいな 私は作曲できるはずです (-)
と (^2)
のようなものを書く diffsq = (^2) . (-)
.
もちろんできません。私が一つのこと できる 2つの引数の代わりにタプルを使用することです (-)
, 、それをで変換することによって uncurry
, 、しかし、これは同じではありません。
私がやりたいことをすることは可能ですか?そうでない場合、私はそれが可能だと思うように誤解していますか?
ノート: これは事実上すでに尋ねられています ここ, 、しかし、答え(私が存在しなければならないと思う)は与えられませんでした。
解決
誤解は、あなたがタイプの関数を考えることです a -> b -> c
返品タイプを使用した2つの引数の関数として c
, 、一方、それは実際には戻りタイプの1つの引数の関数です b -> c
関数タイプが右に関連するため(つまり、それは同じです a -> (b -> c)
. 。これにより、標準の関数構成演算子を使用することが不可能になります。
理由を確認するには、適用してみてください (.)
タイプのオペレーター (y -> z) -> (x -> y) -> (x -> z)
2つの関数のオペレーター、 g :: c -> d
と f :: a -> (b -> c)
. 。これは、統一しなければならないことを意味します y
と c
また b -> c
. 。これはあまり意味がありません。どのようにすることができます y
両方になります c
関数が戻ってきます c
?それは無限のタイプでなければなりません。したがって、これは機能しません。
標準の構成演算子を使用できないからといって、私たちが自分自身を定義するのを止めません。
compose2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
compose2 g f x y = g (f x y)
diffsq = (^2) `compose2` (-)
通常、この場合はポイントフリーのスタイルを使用しないようにして、一緒に行く方が良いです
diffsq a b = (a-b)^2
他のヒント
これに対する私の好みの実装はそうです
fmap . fmap :: (Functor f, Functor f1) => (a -> b) -> f (f1 a) -> f (f1 b)
覚えやすいからです。
fとf1をインスタンス化するとき (->) c
と (->) d
それぞれタイプを取得します
(a -> b) -> (c -> d -> a) -> c -> d -> b
これはタイプです
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
しかし、ガタガタと鳴るのは少し簡単です fmap . fmap
バージョンとそれは他の機能者に一般化されます。
時々これは書かれています fmap fmap fmap
, 、しかし、と書かれています fmap . fmap
より多くの議論を許可するために、より容易に拡張できます。
fmap . fmap . fmap
:: (Functor f, Functor g, Functor h) => (a -> b) -> f (g (h a)) -> f (g (h b))
fmap . fmap . fmap . fmap
:: (Functor f, Functor g, Functor h, Functor i) => (a -> b) -> f (g (h (i a))) -> f (g (h (i b))
等
一般に fmap
それ自体で構成されています n 時間は使用できます fmap
n 深いレベル!
関数はaを形成します Functor
, 、これは配管を提供します n 議論。
詳細については、Conal Elliott'sを参照してください セマンティックエディターの組み合わせ.
これを行う標準的なライブラリ関数はわかりませんが、それを達成するポイントフリーパターンは、構成関数を構成することです。
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
私はこれをコメントで書くつもりでしたが、それは少し長く、それはMightyByteとHammarの両方から引き出されます。
などのオペレーターを中心に標準化することをお勧めします .*
為に compose2
と .**
為に compose3
. 。 MightyByteの定義の使用:
(.*) :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
(.*) = (.) . (.)
(.**) :: (d -> e) -> (a -> b -> c -> d) -> (a -> b -> c -> e)
(.**) = (.) . (.*)
diffsq :: (Num a) => a -> a -> a
diffsq = (^2) .* (-)
modminus :: (Integral a) => a -> a -> a -> a
modminus n = (`mod` n) .* (-)
diffsqmod :: (Integral a) => a -> a -> a -> a
diffsqmod = (^2) .** modminus
はい、 modminus
と diffsqmod
非常にランダムで価値のない機能ですが、それらは迅速でポイントを示していました。別のコンポース関数で構成することにより、次のレベルを定義することがどれほど不気味に簡単かに注目してください(チェーンに似ています fmap
エドワードが言及した)。
(.***) = (.) . (.**)
実際のメモで、から compose12
上向きには、演算子ではなく関数名を書く方が短いです
f .*********** g
f `compose12` g
アスタリスクを数えることは疲れているので、4または5でコンベンションを停止したいかもしれません。
編集]別のランダムなアイデア、使用できます .:
compose2の場合、 .:.
compose3の場合、 .::
compose4の場合、 .::.
compose5の場合、 .:::
Compose6の場合、ドットの数を(最初のドットの後)に視覚的に掘り下げるための引数の数を視覚的にマークさせます。私は星が好きだと思います。
diffsq = ((^ 2) .) . (-)
あなたは考えることができます f . g
に1つの引数を適用するように g
, 、結果を渡します f
. (f .) . g
に2つの引数を適用します g
, 、結果を渡します f
. ((f .) .) . g
に3つの引数を適用します g
, 、 等々。
\f g -> (f .) . g :: (c -> d) -> (a -> b -> c) -> a -> b -> d
コンポジション演算子を何らかの関数でセクションにした場合 f :: c -> d
(の部分的なアプリケーション f
左側)、私たちは取得します:
(f .) :: (b -> c) -> b -> d
したがって、からの関数を期待するこの新しい関数があります b -> c
, 、しかし私たち g
は a -> b -> c
, 、または同等に、 a -> (b -> c)
. 。適用する必要があります a
必要なものを手に入れる前に。さて、もう一度繰り返しましょう:
((f .) .) :: (a -> b -> c) -> a -> b -> d
これが私があなたが望むものを達成するためのエレガントな方法だと思うものです。 Functor
タイプクラスは、関数をコンテナに「プッシュ」する方法を提供し、それを使用して各要素に適用できます fmap
. 。関数を考えることができます a -> b
の容器として b
s各要素はの要素によってインデックス化されています a
. 。したがって、このインスタンスを作成するのは自然なことです。
instance Functor ((->) a) where
fmap f g = f . g
(あなたはそれを手に入れることができると思います import
適切なライブラリを撮影しますが、どちらを思い出せません。)
今、通常の構成 f
と g
些細なことです fmap
:
o1 :: (c -> d) -> (b -> c) -> (b -> d)
f `o1` g = fmap f g
タイプの関数 a -> b -> c
タイプの要素の容器の容器です c
. 。したがって、機能をプッシュする必要があります f
2回ダウンします。どうぞ:
o2 :: (c -> d) -> (a -> (b -> c)) -> a -> (b -> d)
f `o2` g = fmap (fmap f) g
実際には、あなたはあなたが必要としないことに気付くかもしれません o1
また o2
, 、 ただ fmap
. 。そして、あなたが私が忘れていた場所を持っているライブラリを見つけることができれば、あなたはただ使用できると思うかもしれません fmap
追加のコードを書くことなく。