該Functor
類包含一個隱藏的第二構件:
class Functor f where
fmap :: (a -> b) -> f a -> f b
(GHC.Base.<$) :: a -> f b -> f a
文檔:
替換輸入的所有位置具有相同值。默認定義是
fmap . const
,但這可能會被更高效的版本覆蓋。
我想知道更多。爲什麼這個fmap . const
成語是一個單獨的成員?替代實施如何更有效?這個組合器的應用是什麼?
該Functor
類包含一個隱藏的第二構件:
class Functor f where
fmap :: (a -> b) -> f a -> f b
(GHC.Base.<$) :: a -> f b -> f a
文檔:
替換輸入的所有位置具有相同值。默認定義是
fmap . const
,但這可能會被更高效的版本覆蓋。
我想知道更多。爲什麼這個fmap . const
成語是一個單獨的成員?替代實施如何更有效?這個組合器的應用是什麼?
它包括作爲成員,允許用戶自定義爲速度,我猜是因爲它使得它與>>
一致。
我認爲在閱讀器monad ((->) r)
的情況下可能會更快。
x <$ _ = const x
VS
x <$ fa = fmap (const x) fa = (const x) . fa
雖然,這的確是編譯器優化的問題。而且,它似乎沒有爲讀者monad定義基地。
它也可能導致嚴格收藏中的性能提升。即
data Strict a = Strict !a
instance Functor Strict where
fmap f (Strict a) = Strict (f a)
x <$ _ = Strict x
這種不服從法律函子,但儘管如此,你可能會想這樣做在某些情況下。
第三個例子來自無限集合。考慮無限列表
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)
你似乎在答案的後半部分翻轉了'<$' to '$>'。錯字? – huon
@dbaupp:我是這麼認爲的,所以我只是繼續修復它。 –
@dbaupp謝謝,我已經做到了。我喜歡強類型語言的部分原因是它們有助於捕捉那樣的錯誤。 –
下面是一些一對夫婦的代碼片段,我目前正在寫,可能給你你想要的使用這個組合子一個想法:
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解析器定義一個批次當您使用不會產生您關心的值的分析程序(如必需的關鍵字等)時更具可讀性。否則,你必須引入一些額外的pure
或古怪的括號或其他干擾噪音。
至於爲什麼它的類型的類的一部分,通常的原因用於添加否則多餘功能的類型的類是期望某些情況下將能夠優化它,例如(>>)
。由於效率差異取決於實例(這就是整個問題!),那裏沒有單一的答案。儘管如此,我不能立即想到任何明顯的例子,它們將會產生重大差異。
<$
用法又如:
假設你有一個解析器函子P
和parser :: P A
。
f <$> parser
意味着你需要分析的東西,然後應用到f
的結果。
a <$ parser
意味着您不需要解析任何東西(您對結果不感興趣) - 您只需要即可識別,這可以更快。
參見例如regex-applicative庫(請注意使用Void
構造函數)。
基本上,你可能會有一個更有效的用例。如果你不這樣做,你就默認它。這意味着你只關心結構,而不關心價值。 – PyRulez