17

この質問をするのはばかげている気がしますが、しばらく頭に浮かんでいて、答えが見つかりません。

問題は、なぜアプリケーション ファンクターには副作用があるのに、ファンクターにはできないのかということです。

たぶん、彼らはできるのに、私は気づいていません...?

4

4 に答える 4

31

Functorが効果を持たないというのは真実ではありません。すべてApplicative(およびすべてのMonadスルーWrappedMonad)はFunctorです。主な違いは、これらのエフェクトを操作する方法、それらを組み合わせる方法をツールに提供することですApplicativeMonadだいたい

  • Applicativeエフェクトをシーケンスし、内部の値を組み合わせることができます。
  • Monadさらに、前の効果の結果に応じて次の効果を決定することができます。

ただしFunctor、内部の値を変更することしかできず、エフェクトを使用して何かを行うためのツールは提供されません。ですから、何かが正しくFunctorなくApplicativeても、効果がないという意味ではありません。このようにそれらを組み合わせるメカニズムがないだけです。

更新:例として、検討してください

import Control.Applicative

newtype MyF r a = MyF (IO (r, a))

instance Functor (MyF r) where
    fmap f (MyF x) = MyF $ fmap (fmap f) x

これは明らかにFunctor効果をもたらすインスタンスです。に準拠するこれらの効果を使用して操作を定義する方法がないというだけです Applicative。にいくつかの追加の制約を課さない限り、インスタンスrを定義する方法はありません。Applicative

于 2013-01-29T10:57:04.187 に答える
25

この答えは少し単純化しすぎていますが、副作用を前の計算の影響を受ける計算として定義すると、Functor複数の計算を連鎖させる方法がないという理由だけで、型クラスが副作用に対して不十分であることが簡単にわかります。

class Functor f where
    fmap :: (a -> b) -> f a -> f b

ファンクターができる唯一のことは、純粋な関数を介して計算の最終結果を変更することですa -> b

ただし、アプリカティブ ファンクターは 2 つの新しい関数 と を追加しpureます<*>

class Functor f => Applicative f where
    pure   :: a -> f a
    (<*>)  :: f (a -> b) -> f a -> f b

これは、2 つの計算(関数を生成する計算) と、関数が適用されるパラメーターを提供する計算<*>を連鎖できるため、ここでの決定的な違いです 。を使用して、たとえば定義することが可能ですf (a -> b)f apure<*>

(*>) :: f a -> f b -> f b

これは単純に 2 つの計算を連鎖させ、最初の計算の最終結果を破棄します (ただし、「副作用」を適用する可能性があります)。

つまり、計算の可変状態などの効果の最小要件である、計算を連鎖させる機能です。

于 2013-01-29T10:15:41.290 に答える
3

ここでの他の回答は、ファンクターは結合またはシーケンスできないため、副作用を許容しないことを正当に示していますが、これは全体的にはまったく当てはまりますが、ファンクターをシーケンスする方法が 1 つあります。

限定された Writer ファンクターを書きましょう。

data Color    = R    | G    | B
data ColorW a = Re a | Gr a | Bl a deriving (Functor)

Free モナド型をそれに適用する

data Free f a = Pure a | Free (f (Free f a))

liftF :: Functor f => f a -> Free f a
liftF = Free . fmap Pure

type ColorWriter = Free ColorW

red, blue, green :: a -> ColorWriter a
red   = liftF . Re
green = liftF . Gr
blue  = liftF . Bl

もちろん、自由な性質により、これはモナドを形成しますが、効果は実際にはファンクタの「層」から来ています。

interpretColors :: ColorWriter a -> ([Color], a)
interpretColors (Pure a) = ([], a)
interpretColors (Free (Re next)) = let (colors, a) = interpretColors next
                                   in (R : colors, a)
...

ですから、これは一種のトリックです。実際には、「計算」は自由なモナドによって導入されていますが、計算の素材である隠れたコンテキストは、ファンクターによって導入されています。Functor である必要さえありませんが、Functor はそれを構築するための明確な方法を提供します。

于 2013-01-30T01:43:50.013 に答える
2

最初に side EffectsEffectsに名前変更しましょう。あらゆる種類の値に効果があります。ファンクターは、その効果によって生成されるものに関数をマップできるようにするタイプです。

ファンクターが適用可能でない場合、効果に特定の構成スタイルを使用することはできません。(不自然な) 例を挙げてみましょう:

data Contrived :: * -> * where
    AnInt :: Int -> Contrived Int
    ABool :: Bool -> Contrived Bool
    None  :: Contrived a

これは簡単に関手です:

instance Functor Contrived where
    fmap f (AnInt x) = AnInt (f x)
    fmap f (ABool x) = ABool (f x)
    fmap _ None      = None

ただし、 には適切な実装がないpureため、この型は適用可能なファンクターではありません。Maybe結果値が存在しない可能性があるという効果があるという点で似ています。ただし、適用可能なコンビネータを使用して構成することはできません。

于 2013-01-29T07:40:29.427 に答える