11

www.haskell.org の wiki では、Applicative Transformer について次のように説明されています。

では、アプリケーション トランスフォーマーはどこにあるのでしょうか。答えは、アプリカティブ ファンクターは一般的な方法で組み合わせることができるため、特別なトランスフォーマーは必要ないということです。 http://www.haskell.org/haskellwiki/Applicative_functor#Applicative_transformers

たくさんのアプリカティブファンクターを組み合わせるために、次のことを試しました。しかし、私が得たのはたくさんのエラーだけでした。コードは次のとおりです。

import Control.Applicative
import System.IO

ex x y = (:) <$> x <*> y 
test1 = ex "abc" ["pqr", "xyz"]  -- only this works correctly as expected
test2 = ex "abc" [Just "pqr", Just "xyz"]
test3 = ex "abc" (Just "pqr")
test4 = ex (Just 'a') ["pqr", "xyz"]
test5 = ex (return ("abc"):: IO ()) [Just "pqr", Just "xyz"]

これにより、多くの型エラーが発生します。部分的には理解できますが、まったく解決できませんでした。

エラーは最後に示します。

たとえば、Maybe Applicative と List Applicative をどのように組み合わせるのですか?

たとえば、State Applicative と List Applicative を組み合わせるにはどうすればよいですか? たとえば、Maybe と List、Maybe と State を組み合わせて、最後にすべての IO と State アプリケーションを組み合わせた例はありますか?

ありがとう。

GHCi エラー メッセージが続きます。

example.hs:6:19:
    Couldn't match expected type `[Char]' with actual type `Maybe a0'
    In the return type of a call of `Just'
    In the expression: Just "pqr"
    In the second argument of `ex', namely `[Just "pqr", Just "xyz"]'

example.hs:7:19:
    Couldn't match expected type `[[Char]]' with actual type `Maybe a0'
    In the return type of a call of `Just'
    In the second argument of `ex', namely `(Just "pqr")'
    In the expression: ex "abc" (Just "pqr")

example.hs:8:23:
    Couldn't match expected type `Maybe' with actual type `[]'
    In the second argument of `ex', namely `["pqr", "xyz"]'
    In the expression: ex (Just 'a') ["pqr", "xyz"]
    In an equation for `test4': test4 = ex (Just 'a') ["pqr", "xyz"]

example.hs:9:21:
    Couldn't match expected type `()' with actual type `[Char]'
    In the first argument of `return', namely `("abc")'
    In the first argument of `ex', namely `(return ("abc") :: IO ())'
    In the expression:
      ex (return ("abc") :: IO ()) [Just "pqr", Just "xyz"]
Failed, modules loaded: none.
Prelude>
4

4 に答える 4

13

次の型署名を検討してください。

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

組み合わせると、結果のタイプは次のようになります。

liftA2 (<*>) :: (Applicative f, Applicative g) 
             => f (g (a -> b)) -> f (g a) -> f (g b)

これは確かに2つApplicativeの組み合わせです。実際、これは正確に2つのApplicativeの組み合わせです。つまり、 sを一般的な方法で組み合わせることができますが、これは決して自動的にApplicative行われるわけではありません。すべてを明示的に正しい回数持ち上げる必要があります。

あなたの関数は、タイプが。であるexと同等です。あなたの例を見て、あなたがやりたいことについていくつかの推測をします:liftA2 (:)(Applicative f) => f a -> f [a] -> f [a]

test1 = ex "abc" ["pqr", "xyz"]

fこれが、であり、タイプと。[]の引数に適用しています。[Char][[Char]]

test2 = ex "abc" [Just "pqr", Just "xyz"]

2番目の引数はタイプ[Maybe [Char]]なので、2回持ち上げる必要があります。最初の引数もタイプがあり、である必要があるため、解除する必要が[Char]あります[Maybe Char]

test3 = ex "abc" (Just "pqr")

今回は2番目の引数がタイプであるMaybe [Char]ため、リフトは1つだけ必要です。したがって、最初の引数はタイプである必要があります。fMaybeMaybe Char

test4 = ex (Just 'a') ["pqr", "xyz"]

今回は最初の引数はですMaybe Charが、2番目の引数はです[[Char]]。したがって、2つの完全に異なるApplicativesがあります。[Maybe Char]またはを与えるには、両方を持ち上げる必要がありますMaybe [Char]

test5 = ex (return ("abc"):: IO ()) [Just "pqr", Just "xyz"]

ここでの型署名は意味がありません。あなたはおそらく欲しかっIO [Char]た。2番目の引数の型は[Maybe [Char]]です。前の例のように、それらは一致しませんが、今回は3つ Applicativeあります。のようなものが必要な場合は、3回すべてIO [Maybe a]持ち上げる必要があります。(:)liftA2 (liftA2 ex)

sを組み合わせるこの方法はApplicative「ファンクター構成」と呼ばれ、リンク先のページには、明示的な構成型コンストラクターを定義するライブラリーが記載されています。たとえば、ライブラリを使用するtransformersCompose IO (Compose [] Maybe)と、5番目の例を説明するようなタイプにすることができます。この合成タイプはApplicative、前述の一般的な方法でインスタンスとして定義され、正しい数のリフティング操作を適用します。newtype欠点は、これに必要なレイヤーをラップおよびアンラップする必要があることです。


補遺として、このステートメントは次のとおりです。

では、アプリケーショントランスはどこにありますか?答えは、一般的な方法で組み合わせることができるため、アプリケーションファンクタ用の特別なトランスは必要ないということです。

...少し偽物です。2つApplicativeのsの構成も確かですが、これがsApplicativeを組み合わせる唯一の方法ではありません。Applicative

定義は少し異なりますが、StateT s m aと同等のを検討してください。これは、、、、およびの3つのs -> m (s, a)ファンクターの構成として記述することもでき、結果のインスタンスは正しくなりますが、インスタンスは完全に間違っています。代わりに始めた場合、とを作成して定義する方法はありません。((->) s)m((,) s)FunctorApplicativeState s a = s -> (a, s)StateT s mState sm

ここで、非構成の組み合わせは、基本的に、Parsecなどのライブラリで使用される典型的なパーサーコンビネーターモナドの単純化されたバージョンであり、そのようなパーサーは、スタイルのStateT s (Either e)使用が人気のある有名な場所の1つであることに注意してください。そのため、モナド変換子スタイルの組み合わせが、懸念されるApplicative場合、どういうわけか不要または不要であると示唆することは、少し誤解を招くように思われます。Applicative

于 2012-09-25T17:29:54.263 に答える
11

ウィキの記事には、それliftA2 (<*>)を使用してアプリケーション ファンクターを構成できると書かれています。型から使用方法を簡単に確認できます。

o :: (Applicative f, Applicative f1) =>
     f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
o = liftA2 (<*>)

したがって、fisMaybeおよびf1isの場合は次の[]ようになります。

> Just [(+1),(+6)] `o` Just [1, 6] 
Just [2,7,7,12]

逆の場合は次のとおりです。

>  [Just (+1),Just (+6)] `o` [Just 1, Just 6]
[Just 2,Just 7,Just 7,Just 12]

@McCannが言ったように、ex関数は次と同等ですliftA2 (:)

test1 = liftA2 (:) "abc" ["pqr", "xyz"]

より深いアプリケーション スタックで使用するには、次(:)の複数のアプリケーションが必要ですliftA2

*Main> (liftA2 . liftA2) (:) (Just "abc") (Just ["pqr", "xyz"])
Just ["apqr","axyz","bpqr","bxyz","cpqr","cxyz"]

ただし、両方のオペランドの深さが等しい場合にのみ機能します。したがって、 double 以外に、レベルを修正するためにliftA2使用する必要があります。pure

*Main> (liftA2 . liftA2) (:) (pure "abc") (Just ["pqr", "xyz"])
Just ["apqr","axyz","bpqr","bxyz","cpqr","cxyz"]
于 2012-09-25T17:25:20.837 に答える
5

それらを一般的な方法で組み合わせることができると言っても、暗黙的または目に見えない方法で組み合わせることができるという意味ではありません。=)

<*>それでも、とは異なるマンガーを使用するか、新しいタイプのpureノイズを追加することによって、少しコードを書く必要があります。たとえば、TypeComposeパッケージを使用して、次のように記述できます。

test2 = ex (O (Just "abc")) [O (Just "pqr"), O (Just "xyz")]
于 2012-09-25T17:29:17.500 に答える
5

いつものように、ここではタイプに焦点を当てて、適用可能なファンクターの構成が何を意味するのかを理解することが役立ちます。

したがって、副作用のないa特定の純粋な値のタイプについて記述した場合、この純粋な値を、コンビネータを使用して適用可能なファンクターの計算に引き上げることができます。しかし、同じように、インスタンスの関数を使用してファンクターにリフトすることができます。xfpurepuregApplicativepure xg

pure (pure x) :: g (f a)

これが、の効果とのg (f a)効果を組み合わせたタイプの計算です。テストを見ると、gf

test1 :: [String]

で使用した効果は1つだけです。test1つまり、のリストインスタンスがApplicative提供する非決定論です。確かに、それを分解します:

"abc" :: String
((:) <$>) :: [Char] -> [String -> String]
((:) <$> "abc") :: [String -> String]
((:) <$> "abc" <*> ["pqr", "xyz"]) :: [String]

ここで、失敗効果と非決定論効果を構成する場合は、タイプMaybe [a]、またはおそらく。の計算を構築することを期待し[Maybe a]ます。適用可能なファンクターは常に通勤するため、この2つは同等であることがわかります。

これがタイプの計算です[Maybe Char]。不確定にを返しますがChar、返す場合は失敗する可能性があります。

x1 = [Just 'a', Just 'b', Just 'c']

同様に、これがタイプの計算です[Maybe String]

x2 = [Just "pqr", Just "xyz"]

ここで(:)、この結合されたアプリケーションファンクターに移動します。そのためには、2回持ち上げる必要があります。

pure (pure (:)) :: [Maybe (Char -> String -> String)]

同様に、それを適用するには、この計算を両方のファンクターにプッシュする必要があります。したがって、それを行う新しいコンビネータ(<<*>>)を導入できます。

(<<*>>) :: (Applicative f, Applicative f1) =>
           f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
(<<*>>) = liftA2 (<*>)

これで、次のように書くことができます。

pure (pure (:)) <<*>> x1 <<*>> x2

あなたがチェックできるものは期待されるタイプを持っています。

しかし、アプリケーションファンクターは構成の下で閉じられている[Maybe a]ため、それ自体がアプリケーションファンクターであり、再利用できるようにしたいと思うかもしれませpure(<*>)トランスフォーマーパッケージのData.Functor.Composeモジュールは、その方法を示しています。

于 2012-09-25T17:40:41.060 に答える