15

Functorクラスには非表示の 2 番目のメンバーが含まれています。

class Functor f where
  fmap :: (a -> b) -> f a -> f b
  (GHC.Base.<$) :: a -> f b -> f a

ドキュメンテーション:

入力内のすべての場所を同じ値に置き換えます。デフォルトの定義は ですがfmap . const、これはより効率的なバージョンでオーバーライドされる場合があります。

もっと知りたいです。なぜこのfmap . constイディオムは別のメンバーなのですか? 代替の実装はどのように効率的になりますか? このコンビネータのアプリケーションは何ですか?

4

3 に答える 3

11

これは、ユーザーが速度に合わせてカスタマイズできるようにするためのメンバーとして含まれています。これは、と一貫性があるためだと思い>>ます。

リーダーモナドの場合はもっと速いかもしれないと思います((->) r)

x <$ _ = const x

vs

x <$ fa = fmap (const x) fa = (const x) . fa

ただし、これは実際にはコンパイラの最適化の問題です。そして、それはベースのリーダーモナドに対して定義されていないようです。

また、厳密なコレクションのパフォーマンスが向上する可能性があります。つまり、

data Strict a = Strict !a

instance Functor Strict where
   fmap f (Strict a) = Strict (f a)
   x <$ _ = Strict x

これはファンクターの法則には従いませんが、それでも、状況によってはこれを実行したい場合があります。

3番目の例は、無限のコレクションからのものです。無限のリストを検討する

data Long a = Cons a (Long a)

instance Functor Long where
  fmap f (Cons x xs) = Cons (f x) (fmap f xs)

これは問題なく動作しますが、考えてみてください

countUpFrom x = Cons x (countUpFrom (x+1))
ones = 1 <$ (countUpFrom 0)

今、私たちの定義で

ones = 1 <$ (countUpFrom 0)
   = fmap (const 1) (countUpFrom 0) 
   = Cons (const 1 0) (fmap (const 1) (countUpFrom 1)
   = Cons (const 1 0) (Cons (const 1 1) (fmap (const 1) (countUpFrom 2))

つまり、Consこのリストをたどると、大量のセルが割り当てられます。一方、あなたが定義した場合

x <$ _ = let xs = Cons x xs in xs

よりも

ones = 1 <$ countUpFrom 0
 = let xs = Cons 1 xs in xs

結び目を結びました。さらに極端な例には、無限の木があります

data ITree a = ITree a (ITree a) (ITree a)
于 2012-12-30T03:29:31.553 に答える
11

別の<$使用例:

パーサー ファンクタ と があるとPしますparser :: P A

f <$> parser何かを解析してから結果に適用する必要があることを意味しますf

a <$ parserは、何も解析する必要がないことを意味します (結果には関心がありません) 。

たとえば、regex-applicativeVoidライブラリを参照してください (コンストラクタの使用法に注意してください)。

于 2012-12-30T11:20:51.947 に答える
3

このコンビネータを何に使用するかのアイデアを与えるかもしれない、私が現在書いているもののいくつかのコードスニペットを次に示します。

pPrimType = choice
    [ WIPrimIntType <$> flag "unsigned" <*> pIntTypeSize
    , WIPrimFloatType <$> flag "unrestricted" <*> pFloatTypeSize
    , WIPrimBoolType <$ "boolean"
    , WIPrimByteType <$ "byte"
    , WIPrimOctetType <$ "octet"
    ]

pConst = WIConst 
     <$  "const"
     <*> pConstType
     <*> pIdent
     <*  "="
     <*> pConstValue
     <*  semicolon

文字列リテラルが奇妙に見える場合は、OverloadedStrings有効にしていて、他のいくつかのことを実行しているときに文字列に一致するパーサーに変換されているためです (空白を食べる、トークン境界をチェックするなど)。

かなり些細なことのように思えますが、正直なところ、必要なキーワードなどのように、気にする値を生成しないパーサーを使用すると、Applicative-y パーサーの定義がはるかに読みやすくなります。そうしないと、たくさんの余分なpures や奇妙な括弧、またはその他の気を散らすノイズを導入する必要があります。

それが型クラスの一部である理由については、そうでなければ余分な関数を型クラスに追加する通常の理由は、いくつかのインスタンスがそれを最適化できるという期待です(>>)。効率の違いはインスタンスに依存するため (これが要点です!)、単一の答えはありません。ただし、それが大きな違いを生む明らかな例はすぐには思いつきません。

于 2012-12-30T02:58:39.243 に答える