1. ホーム
  2. haskell

[解決済み] Haskellにおける "リフティング "とは?

2022-04-30 02:51:11

質問

リフティングとは何ですか?まず、モナドを理解してから、"lift"が何であるかを理解すべきですか?(私もモナドについて全く無知です :) それとも、どなたか簡単な言葉で説明していただけませんか?

どのように解決するのですか?

リフティングは数学的概念というより、デザインパターンです(今頃、誰かがリフティングがカテゴリか何かであることを示して、私に反論すると思いますが)。

一般的に、あるデータ型とパラメータがあるとします。 次のようなものです。

data Foo a = Foo { ...stuff here ...}

が多く使われていることが分かったとします。 Foo は数値型を取る ( Int , Double など)、これらの数値をラップから外し、足したりかけたりして、ラップに戻すコードを書き続けなければなりません。 これを回避するには、ラップを解除してラップするコードを一度書けばよい。 この関数は伝統的にリフトと呼ばれ、次のように表示されます。

liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c

つまり、2つの引数をとる関数(たとえば (+) 演算子)をFoosの等価関数に変換する。

ということで、次のように書けるようになりました。

addFoo = liftFoo2 (+)

編集:詳細情報

もちろん liftFoo3 , liftFoo4 などがあります。しかし、これは必要ない場合が多い。

観察から始める

liftFoo1 :: (a -> b) -> Foo a -> Foo b

しかし、これでは全く同じ fmap . だから、むしろ liftFoo1 と書くことになります。

instance Functor Foo where
   fmap f foo = ...

もし、本当に完全な規則性を求めるのであれば、次のようにすることができます。

liftFoo1 = fmap

を作ることができれば Foo をファンクタにすることで、おそらく応用ファンクタにすることができます。実際、もしあなたが liftFoo2 とすると、アプリケーティブなインスタンスは次のようになります。

import Control.Applicative

instance Applicative Foo where
   pure x = Foo $ ...   -- Wrap 'x' inside a Foo.
   (<*>) = liftFoo2 ($)

(<*>) 演算子で、Foo は

(<*>) :: Foo (a -> b) -> Foo a -> Foo b

ラップした値に対してラップした関数を適用するのです。そのため、もし liftFoo2 であれば、それを参考に書くことができます。あるいは、直接これを実装して liftFoo2 というのは Control.Applicative モジュールには

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c

があり、同様に liftAliftA3 . しかし、実際には別の演算子があるので、あまり使うことはないでしょう。

(<$>) = fmap

これで書くことができます。

result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4

用語 myFunction <$> arg1 はFooでラップされた新しい関数を返します。

ghci> :type myFunction
a -> b -> c -> d

ghci> :type myFunction <$> Foo 3
Foo (b -> c -> d)

これを次の引数に適用するには、次のようにします。 (<*>) といった具合です。つまり、すべてのアリティにリフト関数があるのではなく、次のような応用のデイジーチェーンがあるだけなのです。

ghci> :type myFunction <$> Foo 3 <*> Foo 4
Foo (c -> d)

ghci: :type myFunction <$> Foo 3 <*> Foo 4 <*> Foo 5
Foo d