簡體   English   中英

為什么在 Applicative 上 pure 的類型是 a -> fa,而不是 (a -> b) -> f (a -> b)?

[英]Why type of pure is a -> f a, not (a -> b) -> f (a -> b) on Applicative?

Pure用於在Applicative容器中將普通的function轉化為function。 有了這個,任何多參數操作都可以在Applicative上使用。 在這種情況下, pure 不希望成為a -> fa類型,它只希望成為(a -> b) -> f (a -> b)類型。 但是pure的類型是a -> fa 為什么要將 normal values 轉化為Applicative use pure more 比轉換函數有更多的目的嗎?

我沒有發現應用提升函數(即(<*>) )的Applicative接口是一個很好的直覺。 由於各種原因,函數的概念化更為復雜。

我更喜歡將Applicative視為提升n元 function

liftA0 :: Applicative f => (a)                -> (f a)
liftA  :: Functor     f => (a -> b)           -> (f a -> f b)
liftA2 :: Applicative f => (a -> b -> c)      -> (f a -> f b -> f c)
liftA3 :: Applicative f => (a -> b -> c -> d) -> (f a -> f b -> f c -> f d)

其中liftA0 = pureliftA已經作為根據Applicative定義的fmap存在。

問題是 0 元和 1 元提升

liftA0 @f @a    :: Applicative f => a -> f a
liftA  @f @a @b :: Applicative f => (a -> b) -> (f a -> f b)

如果我們在 function 類型中實例化liftA0 = pure ,都可以采用a -> b function 類型:

liftA0 @f @(a->b) :: Applicative f => (a -> b) -> f (a->b)
liftA  @f @a @b   :: Applicative f => (a -> b) -> (f a -> f b)

所以pure @f @(a->b)已經有那個類型了。

pure有很多目的,理論上在 Haskell 中被證明是實用的,如果Applicative被視為自然變換類別中的Monoid ,它是單位,與(計算概念作為 Monoids )和Day

type Mempty :: Type -> Type
type Mempty = Identity

type Mappend :: (Type -> Type) -> (Type -> Type) -> (Type -> Type)
type Mappend = Day

mempty :: Applicative f => Mempty ~> f
mempty (Identity a) = pure a

mappend :: Mappend f f ~> f
mappend (LiftA2 (·) as bs) = liftA2 (·) as bs

我剛剛發布了一個適用於Applicative同態的庫,應用同態是尊重應用結構的多態函數。 它為此類結構定義了類型 class

type  Idiom :: k -> (Type -> Type) -> (Type -> Type) -> Constraint
class (Applicative f, Applicative g) => Idiom tag f g where
  idiom :: f ~> g

其中pure初始應用態射

-- https://chrisdone.com/posts/haskell-constraint-trick/
instance (Identity ~ id, Applicative f) => Idiom Initial id f where
  idiom :: Identity ~> f
  idiom (Identity a) = pure a

然后經常使用pure作為計算單位。 它是 Haskell 成功案例之一的Traversable的推動力

instance Traversable [] where
  traverse :: Applicative f => (a -> f b) -> ([a] -> f [b])
  traverse f []     = pure []
  traverse f (x:xs) = ...

我們需要pure是因為我們唯一產生Applicative -action 的參數是fx但對於一個空列表,我們沒有x:: a來提供它。 因此,我們需要 0 元提升。

您可以根據lift:: (a -> b) -> f (a -> b)<*>定義pure:: a -> fa

pure x = lift (const x) <*> lift (const ())

所以這兩種方式都是等價的,而且通常寫成pure更簡單。

(這是 Iceland_jack 對設計原因的出色總結的補充,它應該是這樣的。)

有時從相反的方向來探討問題是值得的。 如果pure的類型是(a -> b) -> f (a -> b)你會得到什么?

作為調用pure的人,這完全是降級。 如前所述, (a -> b) -> f (a -> b)已經是當前pure類型的實例化。 所以在跟注方,你只會失去選擇權。

不過,實現方面是雙重的。 類型越具體,實現的選項就越多。 要求參數為 function 意味着實現可以利用它來做特定於功能的事情。 就像...稱呼它。 這是您可以使用 Haskell 中的函數做的唯一特殊事情。因此,要調用它,您只需要為其提供某種類型的值apure的調用者可以選擇該類型。 你可以通過.. 呃.. 你不能得到其中之一。 唯一的選擇是使用undefined或其他一些具有通用量化類型的底值。 你會怎樣做? pure f = let x = f undefined in... 這對實施 pure 有何幫助?

回到最初的問題,那么:如果pure的類型是(a -> b) -> f (a -> b)你會得到什么? 作為調用者,它的用處不大。 作為一個實施者,它提供了額外的力量,但這種額外的力量並不能幫助你做任何有用的事情。 更具體的類型的好處在哪里?

是的, pure函數比轉換函數有更多的用途。 do塊以調用pure結束是很常見的。 通過將它的使用與<|>結合使用,它也可以方便地提供回退值。

此外,它與基礎范疇論非常吻合; 但我真的不認為這是一個激勵原因。 相反,它首先有用,然后被發現與先前已知的范疇論概念相吻合。 (實際上,從歷史上看,我認為它是“定義一些有用的東西;意識到它是 monad 的概念;發現應用函子的相關概念;意識到它們是有用的。所以它實際上是“有用第一”和“理論第一”的混合體”。但我永遠不會為它的存在辯護,因為它在理論上存在——只會為這個理論很有見地而興奮。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM