簡體   English   中英

如何在“應用”上定義“純”功能?

[英]How to define the `pure` function on `Applicative`?

《第一原理的Haskell編程》一書有一項練習,要求我舉例說明Constant a上的Applicative

我嘗試了這個:

newtype Constant a b =
  Constant { getConstant :: a }
  deriving (Eq, Ord, Show)

instance Functor (Constant a) where
  fmap f (Constant x) = Constant x

instance Monoid a => Applicative (Constant a) where
  pure x = Constant { getConstant = x }
  Constant { getConstant = x } <*> Constant { getConstant = y } =
    Constant { getConstant = x <> y }

這不會編譯。 我潛入了Data.Functor.Constant源代碼 ,它具有以下定義:

pure _ = Constant mempty

即使閱讀了本書中的“應​​用程序”一章,我也不真正理解Applicative及其pure功能。

我知道這可能是一個大問題,但有人可以解釋純粹做什么,以及為什么他們只定義pure使用一些memptyMonoid 以及為什么這個:

pure x = Constant { getConstant = x }

不編譯? 我以為pure會將a類型a某個值轉換為fa類型a某個值,並且在此示例中f只是Constant 為什么它與純粹的Identity a

您的主要麻煩在於種類。 我將嘗試為您解釋為什么您的代碼不起作用,但有關更多詳細信息,我建議閱讀有關種類的出色文章

當您詢問有關ghci中的Applicative的信息時,請考慮返回的第一行:

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

看到f :: * -> *位嗎? 它告訴您期望的種類。 對種類一無所知? 我會給你最簡單的方法。 每次添加參數類型時,基本上就是在告訴您需要“另一種類型來構建類型”。

例如,當您說Maybe a ,您說“要擁有Maybe,我需要一個a”。 或者,當您編寫Either ab ,您說“我的Either類型取決於類型a和類型b”。 您可以通過在ghci中使用:kind:k來獲取有關種類的信息。 考慮:

λ> :kind Bool
Bool :: *

λ> :kind Maybe
Maybe :: * -> *

λ> :kind Either
Either :: * -> * -> *

每個“ *”代表一種類型。 Bool是一種簡單的類型。 Maybe所有人都期待另一種類型。 Either所有人都期待另一種類型。 請注意輸入時的區別:

λ> :kind Maybe Bool
Maybe Bool :: *

現在考慮:

λ> :kind Either Bool
Either Bool :: * -> *

看到這種* -> *嗎? 當詢問有關Applicative信息時,這正是我們所看到的。 (對FunctorMonad也有同樣的期望)。

這意味着typeclass將僅對最新的參數類型進行操作。 對於Either也是如此。 如你看到的:

λ> let fun = (++ " !")
λ> fun <$> Left "Oops"
Left "Oops"

這什么也不做,因為FunctorEither是不是這兩種類型的任一函子:它只有在它的最后型(仿函數bEither ab )。 使用簡單的Functorfmap (或此處的infix版本<$> ),我只能在Either ab的b上進行操作,這就是為什么這個可以起作用的原因:

λ> fun <$> Right "Oops"
Right "Oops !"

現在,回到您想做的事情。 您有一個新的newtype Constant ab ,因此Constant具有* -> * -> * 現在讓我們來看一下:info Applicative的第二行,它將為我們提供pure的簽名:

class Functor f => Applicative (f :: * -> *) where
  pure :: a -> f a

現在,始終考慮到這一點:在a在簽名pure 是不是 aConstant ab 更糟糕的是,在這個例子中, apureb你的Constant 因為如果您考慮善意,並且專門研究此簽名,則會得到:

  pure :: b -> Constant a b

但這不是您在做什么,不是嗎? 新類型中存儲的是a類型。 並且您正在嘗試將類型b放入內部。 由於ab可以不同,所以它將不起作用。

至於“純粹的作用”的“大問題”,這確實是一個問題,我將給您一個答案的開始。

pure是一種使朴素的值a進入Applicative f 如簽名所示: a -> fa 那沒有幫助嗎? 好的,將您的Applicative視為“上下文”(我使用了一個非常籠統的詞,因為Applicatives是一個非常籠統的概念)。 “上下文”可能是:我正在一個可能失敗的世界( Maybe )中工作。 可能是:我在一個對一個問題有很多答案(即List[] )的世界中工作。 它可以有很多很多的東西-在您的示例中,這是一個上下文,在此上下文中,任何內容都不會被計算,並且常量將始終返回。 問題在於,在您的示例中,不可能“猜測”什么是常數。

如我們所見,常量(您的上下文)不是value 這不是apure 它是f的一部分。 這就是為什么實現使用Monoidmempty :您需要一種獲取默認上下文的方法,並且Monoid始終具有可用的默認值。

最后, Applicative 很難 因此,不立即了解它們是完全正常的。 專注於閱讀類型,嘗試了解編譯器告訴您的內容,它將變得更加容易。 慢慢地重讀本章,慢慢來。

看類型。

data Const a b = MkConst a

例如,

> MkConst 1 :: Const Int Bool
> MkConst 1 :: Const Int Float
> MkConst 1 :: Const Int [(Integer, Maybe [()])]

下一個,

instance Functor (Const a) where
  fmap f (MkConst x) = MkConst x       

什么? .... 可是等等; 其實是

instance Functor (Const a) where
 -- fmap :: (b -> c) -> (Const   a b) -> (Const   a c)
    fmap    f           (MkConst x  ) =  (MkConst x  )

現在更清楚了,不是嗎? Const a是一個函子。 在容器范式下,在Functor f => fbf是外殼, b是內部的東西。 f ~ Const a空殼 懷着什么本來可以,但是沒有。 但這僅僅是文學。 在日常的范式和隱喻下,事情不必說得通。 唯一重要的是-類型-適合。

不要試圖把所有的事情都弄清楚。 把它放在紙上。 這是非常重要的建議,我希望自己早一點。 不要跳過任何事情; 根據需要重復多次。

同樣的,

instance Monoid a => Applicative (Const a) where
  -- pure :: Applicative f         => b ->  f         b
  -- pure :: Applicative (Const a) => b -> (Const   a b)
  pure                                x =  (MkConst x  )

等一下 x :: a還是x :: b 擦在其中。 它是后者,但您要使其成為前者。

Const空殼 ,還記得嗎? 即使將x在其中,將x放置在此處仍會保持空白。 換句話說, x本身被忽略,只有它的類型起作用:

  -- pure :: (Monoid a, Applicative (Const a)) => 
  --          b -> (Const   a      b)
  pure        x =  (MkConst mempty  )

(我在這里為類型和數據構造函數使用了不同的名稱,以消除歧義,這一開始可能會造成混淆。)

暫無
暫無

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

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