apay是怎么组成的?

时间:2016-01-05 22:34:27

标签: haskell monads

我曾经写过一个函数,它接受一个值作为输入,调用该输入的函数,如果结果是Just x,它应该返回x;否则,它应该返回原始输入。

换句话说,这个功能(我不知道该怎么称呼):

foo :: (a -> Maybe a) -> a -> a
foo f x = fromMaybe x (f x)

由于它似乎是一个通用功能,我想知道它是否已经定义,所以I asked on TwitterChris Allen replied它是ap fromMaybe

这听起来很有希望,所以我解雇了GHCI并开始尝试:

Prelude Control.Monad Data.Maybe> :type ap
ap :: Monad m => m (a -> b) -> m a -> m b
Prelude Control.Monad Data.Maybe> :type fromMaybe
fromMaybe :: a -> Maybe a -> a
Prelude Control.Monad Data.Maybe> :type ap fromMaybe
ap fromMaybe :: (b -> Maybe b) -> b -> b

ap fromMaybe的类型看起来肯定是正确的,并且一些实验似乎表明它也具有所需的行为。

它是如何运作的?

fromMaybe函数对我来说似乎很清楚,我认为我理解ap的作用 - 至少在Maybe的背景下。当mMaybe时,其类型为Maybe (a -> b) -> Maybe a -> Maybe b

我不明白ap fromMaybe甚至编译的方式。对我来说,这个表达看起来像是部分应用,但我可能会弄错。但是,如果是这种情况,我不明白这些类型是如何匹配的。

ap的第一个参数是m (a -> b),但fromMaybe的类型为a -> Maybe a -> a。那怎么样?编译器推断Monad是哪个m实例?带有两个(curried)参数的fromMaybe如何变成一个只带一个参数的函数?

有人可以帮我连接点吗?

4 个答案:

答案 0 :(得分:20)

但在ap的上下文中使用Maybe 不是。我们将其与函数fromMaybe一起使用,因此它在函数的上下文中,其中

ap f g x = f x (g x)

我们拥有的各种Monad个实例

instance Monad ((->) r)

所以它是

ap :: Monad m =>    m  (a       -> b)  ->    m  a  ->    m  b
fromMaybe     ::  r -> (Maybe r -> r)
ap            :: (r -> (a       -> b)) -> (r -> a) -> (r -> b)   
ap                   f                        g        x :: b
ap  fromMaybe ::                          (r -> a) -> (r -> b)  , a ~ Maybe r , b ~ r

因为类型中的->与右侧相关联:a -> b -> c ~ a -> (b -> c)。试图将类型插入到一起,我们最终只能得到上面的定义。

使用(<*>) :: Applicative f => f (a -> b) -> f a -> f b,我们可以将其写为(fromMaybe <*>),如果你喜欢这种涂鸦:

 #> :t (fromMaybe <*>)
(fromMaybe <*>) :: (r -> Maybe r) -> r -> r

正如在这里的另一个答案中正确地指出的那样,当与函数一起使用时,<*>只是你的好人。 S combinator。我们不能在Haskell中使用名为S的函数,因此<*>只是无点编码风格的标准集的一部分。 Monadic bind(更多的是,翻转),=<<,可能更加神秘,但是一个无点编程人员并不关心,并乐意用它来编码另一个类似的模式,

(f =<< g) x = f (g x) x 

combinatory函数调用中,神秘或无神秘(zipWith (-) =<< drop 1浮现在脑海中)。

答案 1 :(得分:16)

为简洁和机械的答案道歉。我不喜欢樱桃挑选像Applicative或Monad这样的东西,但我不知道你在哪里。这是not my usual approach to teaching Haskell

首先,ap真的是(<*>)

Prelude> import Control.Monad
Prelude> import Data.Maybe
Prelude> import Control.Applicative
Prelude> :t ap
ap :: Monad m => m (a -> b) -> m a -> m b
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

这是什么意思?这意味着我们不需要像Monad那样强大的东西来描述我们正在做的事情。适用就足够了。但是,Functor没有。

Prelude> :info Applicative
class Functor f => Applicative (f :: * -> *) where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
Prelude> :info Functor
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b

以下是ap / (<*>)与Maybe Monad / Applicative:

Prelude> ap (Just (+1)) (Just 1)
Just 2
Prelude> (<*>) (Just (+1)) (Just 1)
Just 2

要弄清楚的第一件事是,我们正在讨论类型类Applicative的哪个实例?

Prelude> :t fromMaybe
fromMaybe :: a -> Maybe a -> a

从Maye的类型中汲取一些东西给我们:

(->) a (Maybe a -> a)

所以我们在这里关注的类型构造函数是(->)。 GHCi告诉我们有关(->)也称为函数类型的内容是什么?

Prelude> :info (->)
data (->) a b   -- Defined in ‘GHC.Prim’
instance Monad ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Applicative ((->) a) -- Defined in ‘GHC.Base’

槽糕。那可能呢?

Prelude> :info Maybe
data Maybe a = Nothing | Just a     -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Applicative Maybe -- Defined in ‘GHC.Base’

使用(<*>)对于Maybe的行为是:

Prelude> (+1) 1
2
Prelude> (+1) `fmap` Just 1
Just 2
Prelude> Just (+1) <*> Just 1
Just 2
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> let mFmap = fmap :: (a -> b) -> Maybe a -> Maybe b
Prelude> (+1) `mFmap` Just 1
Just 2
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude> let mAp = (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
Prelude> :t (+1)
(+1) :: Num a => a -> a
Prelude> :t Just (+1)
Just (+1) :: Num a => Maybe (a -> a)
Prelude> Just (+1) `mAp` Just 1
Just 2

好的,函数类型的Functor和Applicative怎么样?这里一个棘手的部分是(->)部分应用类型作为Functor / Applicative / Monad。因此,f成为整体(->) a的{​​{1}},其中(->) a b是参数类型,结果为a

b

不保证有用。您可以从类型中了解Prelude> (fmap (+1) (+2)) 0 3 Prelude> (fmap (+1) (+2)) 0 3 Prelude> :t fmap fmap :: Functor f => (a -> b) -> f a -> f b Prelude> let funcMap = fmap :: (a -> b) -> (c -> a) -> c -> b Prelude> -- f ~ (->) c Prelude> (funcMap (+1) (+2)) 0 3 Prelude> :t (<*>) (<*>) :: Applicative f => f (a -> b) -> f a -> f b Prelude> let funcAp = (<*>) :: (c -> a -> b) -> (c -> a) -> (c -> b) Prelude> :t fromMaybe fromMaybe :: a -> Maybe a -> a Prelude> :t funcAp fromMaybe funcAp fromMaybe :: (b -> Maybe b) -> b -> b Prelude> :t const const :: a -> b -> a Prelude> :t funcAp const funcAp const :: (b -> b1) -> b -> b 并不感兴趣,并了解参数化的工作原理。

编辑:说到撰写,funcAp const的Functor只是(->) a。适用的是,但有一个额外的论点。 Monad是应用程序,但有论点翻转。

进一步的问题:(.)的适用<*>是S,而(->) a是SKI组合子微积分的K. (您可以从K和S派生出来。实际上,您可以从K和S派生出任何程序。)

pure

答案 2 :(得分:8)

为了清楚起见,我将重新标记类型参数。

ap :: Monad m => m (a -> b) -> m a -> m b
fromMaybe :: c -> Maybe c -> c
  

编译器推断m是哪个Monad实例?

((->) r)Monad。对于某些特定的 r,这是所有以r类型为参数的函数。

所以在类型中:

ap :: Monad m => m (a -> b) -> m a -> m b

m(c ->)aMaybe cbc

返回类型m a -> m b扩展为(c -> Maybe c) -> c -> c - 这是ap fromMaybe的类型。

答案 3 :(得分:6)

如果您更喜欢中缀语法,那么您要查找的monad是(->) rr -> _

然后ap的签名扩展为:

 m (a -> b) -> m a -> m b =
 (r -> (a -> b)) -> (r -> a) -> r -> b = -- now we use the signature of fromMaybe 
 (b -> (Maybe b -> b)) -> (b -> Maybe b) -> b -> b

现在,如果您将ap fromMaybe视为部分应用的函数,那么您将获得所需的结果。