2012-12-30 15 views
9

Functorクラスは、隠された第二の部材が含まれています

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

ドキュメント:

が同じ値を持つ入力におけるすべての位置を交換します。デフォルトの定義はfmap . constですが、これはより効率的なバージョンで上書きされる可能性があります。

もっと知りたいです。なぜこのfmap . constイディオムは別のメンバーですか?代わりの実装がより効率的になる方法は?このコンビネータのアプリケーションは何ですか?文字列リテラルは、私がOverloadedStringsを持っているからだと奇妙に見える場合

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

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

:ここ

+0

本質的に、より効率的な使用例があるかもしれません。そうしないと、デフォルトのままになります。つまり、価値観ではなく、構造だけを気にするということです。 – PyRulez

答えて

8

ユーザーがスピードのためにカスタマイズできるようにするメンバーとして含まれており、>>と一貫していると考えられます。

リーダーモナド((->) r)の方が速いかもしれないと思います。

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

x <$ _ = const x 

、これは実際にコンパイラの最適化の問題ですが。そして、それは基盤の読者モナドのために定義されているようには見えません。

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

data Strict a = Strict !a 

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

これはファンクタの法則に従わないが、状況によってはこれを行うこともできます。

第3の例は、無限のコレクションに由来します。ある

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)) 

に拡大すると私たちの定義と、無限リスト正常に動作します

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) 

と思う、それはの全体の束を割り当てますConsのセルを使用してください。一方、あなたが結婚した

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

より

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

を定義した場合、しばらく。もっと極端な例では、無限の木

data ITree a = ITree a (ITree a) (ITree a) 
+0

答えの後半に「<$' to '$>」を反転させたようです。打ち間違え? – huon

+0

@dbaupp:私はそう思ったので、先に進んで固定しました。 –

+0

@dbauppありがとう、私はそれをしていた。強く型付けされた言語が好きな理由の1つは、そのようなエラーを捕捉するのに役立つということです。 –

3

は、私は現在、それを書いている何かから夫婦のコードスニペットは、あなたがこのコンビネータを使用すると思いますかのアイデアを与えるかもしれないです有効になっており、それらはいくつか他のものをしながら文字列に一致するパーサーに変換されている(、トークンの境界をチェックし、空白を食べ& Cを。)

それはかなり些細なようだが、正直なところ、それはApplicative -yパーサの定義になりlotあなたが必要とするキーワードなどの気にかけた価値を生み出さないパーサで導くと、より読みやすくなります。それ以外の場合は、pureの括弧や奇妙なカッコやその他の混乱するノイズを導入する必要があります。

なぜそれが型クラスの一部であるのかというと、型クラスに余計な関数を追加する通常の理由は、いくつかのインスタンスがそれを最適化できるという期待です。 (>>)。効率の差はインスタンスに依存するので(それは全体のポイントです!)、そこには一つの答えはありません。私は、それが大きな違いを生む明白な事例はすぐに考えることができません。

7

<$使用方法の別の例が付属しています:

あなたはパーサファンクタP、およびparser :: P Aを持っていると仮定します。

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

a <$ parserは(あなたは結果に興味を持っていないなら)あなたは何を解析する必要がないことを意味 - あなただけのはるかに高速にすることができを、認識する必要があります。

regex-applicativeライブラリ(Voidコンストラクタの使用に注意してください)。

関連する問題