fmap 如何与数据构造函数一起使用?

How can fmap be used with a data constructor?

我正在尝试理解一些 haskell 代码。

这是有道理的。

Prelude> fmap (+1) (Just 1)
Just 2

这也有道理。

Prelude> (fmap.fmap) (+1) (Just [1])
Just [2]

但我不明白这是怎么回事。

Prelude> (fmap.fmap) (+1) Just 1
Just 2

我试过把零件拼出来。在我看来,这就是正在发生的事情。

(fmap (fmap (+1)) Just) 1

我尝试输入子表达式。

这是有道理的。

Prelude> :t fmap (+1)
fmap (+1) :: (Functor f, Num b) => f b -> f b

这还是有道理的。

Prelude> :t fmap (fmap (+1))
fmap (fmap (+1)) :: (Functor f, Functor f1, Num b) =>
    f (f1 b) -> f (f1 b)

但是我不明白这个。

Prelude> :t fmap (fmap (+1)) Just
fmap (fmap (+1)) Just :: Num b => b -> Maybe b

类型的函数是如何实现的
(Functor f, Functor f1, Num b) => f (f1 b) -> f (f1 b)

应用 Just 后,其类型为:

a -> Maybe a

这种类型的结果?

Num b => b -> Maybe b

问题confused about function as instance of Functor in haskell可能与此有关,但我仍然很困惑。

f 解析为仿函数 (->) af1 解析为 Maybe,因为

Just :: (->) a (Maybe a)

因此,如果我们使用上述绑定编写 fmap (fmap (+1)) 的类型,我们将得到:

fmap (fmap (+1)) :: Num b => (->) a (Maybe b) -> (->) a (Maybe b)

重写 (->) 作为中缀构造函数我们得到:

fmap (fmap (+1)) :: Num b => (a -> Maybe b) -> (a -> Maybe b)

现在我们将它应用到 Just :: a -> Maybe a 所以我们得到

fmap (fmap (+1)) Just :: Num a => a -> Maybe a

您写 Just 1 而不是 (Just 1) 因此,这是两个独立的参数。我们现在可以用更规范的形式重写它,例如:

   (fmap . fmap) (+1) Just 1
-> (\x -> fmap (fmap x)) (+1) Just 1
-> ((fmap (fmap (+1)) Just) 1

现在我们可以分析类型了:

fmap<i>1</i> :: Functor f => (a -> b) -> f a -> f b
fmap<i>2</i> :: Functor g => (c -> d) -> g c -> g d
(+1) :: Num h => h -> h
Just :: i -> Maybe i
1 :: Num j => j

其中fmap<i>i</i>是第i-thfmap表达式(如果我们读取它 left-to-right)。如果我们知道执行一些分析,我们会看到因为我们使用 fmap (+1),所以我们知道 c ~ d ~ h:

fmap<i>1</i> :: Functor f => (a -> b) -> f a -> f b
fmap<i>2</i> :: Functor g => (h -> h) -> g h -> g h
(+1) :: Num h => h -> h
Just :: i -> Maybe i
1 :: Num j => j

然后我们看到第一个 fmap (fmap<i>1</i>) 被调用 fmap (+1) :: Functor g => g h -> g h 作为第一个参数,Just :: i -> Maybe i 作为第二个参数。因此,如果我们进一步执行类型分析,我们得到:(a -> b) ~ g h -> g h,所以 a ~ b ~ g h,并且我们知道 f (g h) ~ i -> Maybe i,所以这意味着 f (g h) ~ (->) i (Maybe i) 所以 f ~ (->) ig ~ Maybe, h ~ i, 此外 i ~ j:

fmap<i>1</i> :: (Maybe i -> Maybe i) -> (->) i (Maybe i) -> (->) i Maybe i
fmap<i>2</i> :: (i -> i) -> Maybe i -> Maybe i
(+1) :: Num h => i -> i
Just :: i -> Maybe i
1 :: Num i => i

现在这里的一个关键方面是 (->) r 也是一个函子,确实在 base-4.10.1.0 source code 中我们看到:

instance Functor ((->) r) where
    fmap = (.)

我们可以在这里将函数视为函子,如果我们执行 fmap 我们 "post process" 结果。所以这意味着 我们应用 Just 之后,我们应用 fmap (+1) 到那个结果。所以第一个 fmap 相当于 (.) 而第二个 fmap 超过 Maybe,结果我们得到:

   ((fmap (fmap (+1)) Just) 1
-> (((.) (fmap (+1)) Just) 1
-> ((\x -> (fmap (+1) (Just x)) 1
-> fmap (+1) (Just 1)
-> Just 2

所以简而言之,我们使用 (fmap (+1)) 作为 post 处理 步骤, 我们应用 Just1.