2017-04-27 65 views
2

的確如此:爲什麼Applicative應該繼承Functor?

λ :i Applicative 
class Functor f => Applicative (f :: * -> *) where 

同時:

fmap f x = pure f <*> x 

- 由Applicative規律,我們可以從pure & <*>定義fmap

我不明白爲什麼我要我要一個Applicative如果,真的,fmap可以自動在pure<*>規定設立每次不厭其煩地定義fmap

我收集這將是必要的,如果pure<*>在某種程度上取決於fmap的定義,但我不明白他們爲什麼必須。

+3

你想讓你的應用程序使用'fmap'嗎?由於你描述的微不足道的實現,它沒有理由繼承'Functor'。 – 4castle

+0

是的,定義'fmap'是關於最簡單的事情,你可以用任何可能的類型來完成。 – leftaroundabout

回答

1

雖然有建議,使其更容易https://ghc.haskell.org/trac/ghc/wiki/IntrinsicSuperclasses 「默認實例」問題本身非常困難。

的一個挑戰是如何應對常見的超:

fmap f x = pure f <*> x       -- using Applicative 
fmap f x = runIdentity (traverse (Identity . f) x) -- using Traversable 
fmap f x = x >>= (return . f)    -- using Monad 

哪一個來接?

所以我們現在能做的最好的就是提供fmapDefault(如Data.Traversable)那樣;或使用pure f <*> x;或fmapRep,從Data.Functor.Rep適用。

+1

'fmap f x = x >> =(return。f)' – amalloy

7

雖然fmap可以從pure<*>得出,但它通常不是最有效的方法。比較:

fmap :: (a -> b) -> Maybe a -> Maybe b 
fmap f Nothing = Nothing 
fmap f (Just x) = Just (f x) 

使用應用型工具所做的工作:

fmap :: (a -> b) -> Maybe a -> Maybe b 
-- inlining pure and <*> in: fmap f x = pure f <*> x 
fmap f x = case (Just f) of 
    Nothing -> Nothing 
    Just f' -> case x of 
    Nothing -> Nothing 
    Just x' -> Just (f' x') 

在構造函數中無意義的東西包裹起來只是做一個模式匹配反對。

因此,顯然能夠獨立於應用功能來定義fmap是有用的。那可能可以通過使用所有三個功能的單個類型類型來完成,使用可以覆蓋的默認實現fmap。但是,有些類型可以製作出很好的Functor實例,但不是很好的Applicative實例,因此您可能只需要實現一個實例。因此,兩個類型類。

而且由於沒有適用實例但沒有Functor實例的類型,您應該可以像處理Functor一樣對待Applicative,如果您喜歡的話;因此兩者之間的延伸關係。

但是,如果執行函子的輪胎,你可以(在大多數情況下)要求GHC導出唯一可能實現的函子的你,與

{-# LANGUAGE DeriveFunctor #-} 
data Boring a = Boring a deriving Functor 
+0

在大多數情況下,'數據Boring a =無聊派生Functor',一個獨立的派生實例Functor Boring'仍然可以工作。 GHC不能以某種方式派生函子實例,即使在這種情況下,花生也會手工編寫它,而與'Applicative'實例相比,真的很少有一個函數。 – leftaroundabout

+0

我希望GHC用'-O'來簡化嵌套的情況,因爲...''總是選擇'Just'分支。事實上SPJ意味着它實際上在這個演講中做了這個優化https://youtu.be/uR_VzYxvbxg?t=43m59s。當然,它不一定能夠爲所有應用程序進行重寫。 –