首页 > 解决方案 > Haskell fmap 组成误区

问题描述

如果我组成两个fmaps

Prelude> :t (fmap.fmap)
(fmap.fmap)
  :: (Functor f, Functor f1) => (a -> b) -> f1 (f a) -> f1 (f b)

我得到一个函数,它将函数应用于 2 个嵌套结构层内的值,f1并且f.

我可以使用它——这正如我所料:

Prelude> (fmap.fmap) (+1) [[1,2]]
[[2,3]]

正如我预期的那样推断类型(结果周围的2级结构)

Prelude> :t  (fmap.fmap) (+1) [[1,2]]
(fmap.fmap) (+1) [[1,2]] :: Num b => [[b]]

以下不起作用。我也期待这一点(因为我们不能申请sum单个号码):

Prelude>  (fmap.fmap) sum [[1,2]]

<interactive>:39:2: error:
    • Could not deduce (Num (t0 b))
      from the context: (Num (t b), Num b, Foldable t)
        bound by the inferred type for ‘it’:
                   (Num (t b), Num b, Foldable t) => [[b]]
        at <interactive>:39:2-24
      The type variable ‘t0’ is ambiguous
    • In the ambiguity check for the inferred type for ‘it’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      When checking the inferred type
        it :: forall (t :: * -> *) b.
              (Num (t b), Num b, Foldable t) =>
              [[b]]
Prelude> :t  (fmap.fmap) sum [[1,2]]
(fmap.fmap) sum [[1,2]] :: (Num (t b), Num b, Foldable t) => [[b]]

但!如果我将一级结构更改为一种Maybe类型:

Prelude> (fmap.fmap) sum Just [1,2]
Just 3

然后它开始工作,但在我看来打破了类型签名 (fmap.fmap) :: (Functor f, Functor f1) => (a -> b) -> f1 (f a) -> f1 (f b) (因为它将sum函数应用到结构的第一级,而不是我预期的第二级)。

我认为在我理解函数应用程序顺序如何评估这里存在问题,因为我发现使用括号,这在具有可折叠列表值的两个结构级别内按预期工作(与第一个示例中的数字相比):

Prelude> (fmap.fmap) sum (Just [[1,2],[2,3]])
Just [3,5]

但是这里会发生什么:

Prelude> (fmap.fmap) sum Just [1,2]
Just 3
  1. 为什么跳过第一级结构?

  2. 这里函数应用的顺序是什么?

  3. Haskell 如何推断最终类型?

     Prelude> :t (fmap.fmap) sum Just [1,2]
     (fmap.fmap) sum Just [1,2] :: Num t => Maybe t
    

为什么Maybe t而不是Maybe List t我理解的(fmap.fmap)必须确定f1 (f b)两个层次的结构而不是一个?

标签: haskell

解决方案


Int让我们计算一下,为了简单起见,假设数字文字是s。

(fmap.fmap) sum Just [1,2]
= fmap (fmap sum) Just [1,2]
        |         |    \ -- an additional argument applied to the result of fmap
        |         \ -- the value with a type of the form f a with f Functor
        \ -- the function to fmap

这里,Just是一个函数[Int] -> Maybe [Int],所以第一个fmap对函子进行操作f = (->) [Int],我们有,fmap = (.)因为它是这样定义的Functor ((->) [Int])

= (.) (fmap sum) Just [1,2]
= (fmap sum) (Just [1,2])

现在,fmap f (Just x) = Just (f x)既然这Functor Maybe就是定义的方式。

= Just (sum [1,2])
= Just 3
  1. 为什么跳过第一级结构?

它不是。第一级是(->) [Int]

  1. 这里函数应用的顺序是什么?

平常的那一个。fmap.fmap应用于sum。结果应用于Just. 最终结果应用于[1,2].

  1. Haskell 如何推断最终类型?

它看到这Just是一个“包装在(->) [Int]函子内的值”,并使用它来实例化第一个fmap. 第二个是在级别fmap上使用,因为返回它。MaybeJust


推荐阅读