Haskell - 评估(+)<$>(+3)<*>(*100)$ 5

rjs*_*rjs 11 haskell functor applicative

Lipovača表示,从"了解你是一个很好的Haskell"中的章节开始:

"当我们这样做(+) <$> (+3) <*> (*100),我们正在做的是将使用功能+上的结果(+3)(*100)并返回.为了证明在一个真实的例子,当我们做(+) <$> (+3) <*> (*100) $ 55第一次得到应用(+3)(*100),导致8500.然后,+被调用8并且500,导致508."

但是,如果我自己尝试评估函数,请考虑这个函数的定义:( - >)r):

instance Applicative ((->) r) where  
    pure x = (\_ -> x)  
    f <*> g = \x -> f x (g x)  
Run Code Online (Sandbox Code Playgroud)

我把以上表达式的评价读作:

(\x -> (3 + x) (100 * x)) $ 5

但是我没有看到我们如何将两个部分应用的二进制函数组合成一个lambda(事实上,GHCi抛出一个无限类型的错误,试图将它绑定到一个变量).此外,对于工作解释,如果我们查看<$>我们得到的类型定义:

(<$>) :: Functor f => (a -> b) -> f a -> f b

或者更具体地说,我们可以将其提升视为:

(<$>) :: Functor f => (a -> b) -> (f a -> f b)

考虑到在这种情况下我们的仿函数是(( - >)r),我可以推断这是在先前的评估中发生的变换(假设左关联性首先发生,而不是正确的关联应用程序5):

(\x -> a + b)其中a= (+ 3)b= (* 100).这是应该返回的函数.但是,我是否认为这是最终(粗略)形式?

(\x -> (3 + x) + (100 * x)) $ 5

...产生508.

我发现Lipovača的描述在表达式如何工作方面更容易理解,但我的直觉告诉我,对于Haskell编译器引擎盖下的gorey细节并不完全正确.我更容易认为(+)的fmap首先发生了一个带有两个仿函数的函数,这两个仿函数是部分应用的函数,它们接受共享输入,然后我们为它应用了一个值.我们可以这样做,因为懒惰的评估.这是错的吗?

Dav*_*vid 16

首先,请注意左侧<$><*>左侧相关联.内部没有任何神奇的事情发生,我们可以看到基本上是一系列eta扩展和beta减少的转换.一步一步,它看起来像这样:

(((+) <$> (+3))         <*> (*100)) $ 5        -- Add parens
((fmap (+) (+3))        <*> (*100)) $ 5        -- Prefix fmap
(((+) . (+3))           <*> (*100)) $ 5        -- fmap = (.)
((\a -> (+) ((+3) a))   <*> (*100)) $ 5        -- Definition of (.)
((\a -> (+) (a+3))      <*> (*100)) $ 5        -- Infix +
((\a b -> (+) (a+3) b)) <*> (*100)) $ 5        -- Eta expand
(\x -> (\a b -> (+) (a+3) b) x ((*100) x)) $ 5 -- Definition of (<*>)
(\x -> (\a b -> (+) (a+3) b) x (x*100)) $ 5    -- Infix *
(\a b -> (+) (a + 3) b) 5 (5*100)              -- Beta reduce
(\a b -> (a + 3) + b)   5 (5*100)              -- Infix +
(5 + 3) + (5*100)                              -- Beta reduce (twice)
508                                            -- Definitions of + and *
Run Code Online (Sandbox Code Playgroud)

有点令人困惑的是,$与右边相关联的事实与其固定性为0的事实关系不大.如果我们定义一个新的运算符,我们可以看到这一点:

(#) :: (a -> b) -> a -> b
f # a = f a
infixl 0 #
Run Code Online (Sandbox Code Playgroud)

在GHCi中:

?> (+) <$> (+3) <*> (*100) # 5
508
Run Code Online (Sandbox Code Playgroud)