Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Function fmap fmap如何应用于函数(作为参数)?_Function_Haskell_Types_Functor_Function Composition - Fatal编程技术网

Function fmap fmap如何应用于函数(作为参数)?

Function fmap fmap如何应用于函数(作为参数)?,function,haskell,types,functor,function-composition,Function,Haskell,Types,Functor,Function Composition,我试图理解fmap-fmap如何应用于say(*3)这样的函数 fmap-fmap的类型: (fmap fmap):: (Functor f1, Functor f) => f (a -> b) -> f (f1 a -> f1 b) fmap :: Functor f => (a -> b) -> f a -> f b |______| |________|

我试图理解
fmap-fmap
如何应用于say
(*3)
这样的函数

fmap-fmap
的类型:

(fmap fmap):: (Functor f1, Functor f) => f (a -> b) -> f (f1 a -> f1 b)
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|    |________|
                         |            |
                      domain      codomain
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|
                         |
                       fmap :: Functor g => (x -> y) -> g x -> g y
                                            |______|    |________|
                                                |            |
                                                a    ->      b
fmap fmap :: (Functor f, Functor g) => f (x -> y) -> f (g x -> g y)
(*3)
的类型:

这意味着签名
a->a
对应于
f(a->b)
,对吗

Prelude> :t (fmap fmap (*3))
(fmap fmap (*3)):: (Num (a -> b), Functor f) => (a -> b) -> f a -> f b
我尝试创建一个简单的测试:

test :: (Functor f) => f (a -> b) -> Bool 
test f = True
并将
(*3)
输入其中,但我得到:

*Main> :t (test (*3))

<interactive>:1:8:
    No instance for (Num (a0 -> b0)) arising from a use of ‘*’
    In the first argument of ‘test’, namely ‘(* 3)’
    In the expression: (test (* 3))
*Main>:t(测试(*3))
:1:8:
没有因使用“*”而产生的(Num(a0->b0))实例
在“test”的第一个参数中,即“(*3)”
在表达式中:(test(*3))

为什么会发生这种情况?

当你不知道自己在做什么时,多态性是危险的。
fmap
(*)
都是多态函数,盲目使用它们会导致代码非常混乱(甚至可能不正确)。我以前也回答过类似的问题:

在这种情况下,我相信查看值的类型可以帮助您找出哪里出了问题,以及如何纠正问题。让我们从
fmap
的类型签名开始:

(fmap fmap):: (Functor f1, Functor f) => f (a -> b) -> f (f1 a -> f1 b)
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|    |________|
                         |            |
                      domain      codomain
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|
                         |
                       fmap :: Functor g => (x -> y) -> g x -> g y
                                            |______|    |________|
                                                |            |
                                                a    ->      b
fmap fmap :: (Functor f, Functor g) => f (x -> y) -> f (g x -> g y)
fmap
的类型签名很容易理解。它将函数从
a
提升到
b
到一个函子的上下文中,不管该函子是什么(例如,列表,可能,或者,等等)

“域”和“编码域”分别表示“输入”和“输出”。无论如何,让我们看看当我们将
fmap
应用到
fmap
时会发生什么:

(fmap fmap):: (Functor f1, Functor f) => f (a -> b) -> f (f1 a -> f1 b)
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|    |________|
                         |            |
                      domain      codomain
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|
                         |
                       fmap :: Functor g => (x -> y) -> g x -> g y
                                            |______|    |________|
                                                |            |
                                                a    ->      b
fmap fmap :: (Functor f, Functor g) => f (x -> y) -> f (g x -> g y)
如您所见,
a:=x->y
b:=gx->gy
。此外,还添加了
函子g
约束。这为我们提供了
fmap-fmap
的类型签名:

(fmap fmap):: (Functor f1, Functor f) => f (a -> b) -> f (f1 a -> f1 b)
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|    |________|
                         |            |
                      domain      codomain
fmap :: Functor f => (a -> b) -> f a -> f b
                     |______|
                         |
                       fmap :: Functor g => (x -> y) -> g x -> g y
                                            |______|    |________|
                                                |            |
                                                a    ->      b
fmap fmap :: (Functor f, Functor g) => f (x -> y) -> f (g x -> g y)
那么,
fmap-fmap
做什么呢?第一个
fmap
将第二个
fmap
提升到函子
f
的上下文中。假设
f
可能
。因此,关于专业化:

fmap fmap :: Functor g => Maybe (x -> y) -> Maybe (g x -> g y)
fmap fmap :: Maybe (x -> y) -> Maybe ([x] -> [y])
因此,
fmap-fmap
必须应用于
值,该值可能包含一个函数。
fmap-fmap
的作用是将
值中的函数提升到另一个functor
g
的上下文中。假设
g
[]
。因此,关于专业化:

fmap fmap :: Functor g => Maybe (x -> y) -> Maybe (g x -> g y)
fmap fmap :: Maybe (x -> y) -> Maybe ([x] -> [y])
如果我们将
fmap-fmap
应用于
Nothing
,则得到
Nothing
。然而,如果我们将其应用于
Just(+1)
,那么我们将得到一个函数,该函数增加列表中的每个数字,并封装在
Just
构造函数中(即,我们得到
Just(fmap(+1))

然而,
fmap-fmap
更一般。它实际上做的是查看一个函子
f
(无论
f
可能是什么),并将
f
中的函数提升到另一个函子
g
的上下文中

到目前为止还不错。那有什么问题?问题是当您将
fmap
应用于
(*3)
时。这是愚蠢和危险的,就像喝酒和开车一样。让我告诉你为什么这是愚蠢和危险的。查看
(*3)
的类型签名:

当您将
fmap-fmap
应用于
(*3)
时,函子
f
专用于
(>)r
(即函数)。函数是有效的函子。
(>)r
fmap
函数只是函数组合。因此,专业化上的
fmap-fmap
类型为:

fmap fmap :: Functor g => (r -> x -> y) -> r -> g x -> g y

-- or

(.)  fmap :: Functor g => (r -> x -> y) -> r -> g x -> g y
                          |___________|
                                |
                              (*3) :: Num a => a ->    a
                                               |       |
                                               |    ------
                                               |    |    |
                                               r -> x -> y
你明白为什么这是愚蠢和危险的吗

  • 这是愚蠢的,因为您正在将一个需要两个参数(
    r->x->y
    )的输入函数应用到一个只有一个参数(
    (*3))的函数:Num a=>a->a
  • 这很危险,因为
    (*3)
    的输出是多态的。因此,编译器不会告诉您正在做一些愚蠢的事情。幸运的是,因为输出是有界的,所以您得到了一个类型约束
    Num(x->y)
    ,它应该表明您在某个地方出错了
  • 计算类型,
    r:=a:=x->y
    。因此,我们得到以下类型签名:

    fmap . (*3) :: (Num (x -> y), Functor g) => (x -> y) -> g x -> g y
    
    fmap . (*) :: (Num a, Functor g) => a -> g a -> g a
    
    test (*3) :: Num (a -> b) => Bool
    
    让我告诉你为什么使用值是错误的:

      fmap . (*3)
    = \x -> fmap (x * 3)
                 |_____|
                    |
                    +--> You are trying to lift a number into the context of a functor!
    
    您真正想要做的是将
    fmap-fmap
    应用到
    (*)
    ,这是一个二进制函数:

    (.) fmap :: Functor g => (r -> x -> y) -> r -> g x -> g y
                             |___________|
                                   |
                                  (*) :: Num a => a -> a -> a
                                                  |    |    |
                                                  r -> x -> y
    
    test :: Functor f => f (a -> b) -> Bool
    
    因此,
    r:=x:=y:=a
    。这将为您提供类型签名:

    fmap . (*3) :: (Num (x -> y), Functor g) => (x -> y) -> g x -> g y
    
    fmap . (*) :: (Num a, Functor g) => a -> g a -> g a
    
    test (*3) :: Num (a -> b) => Bool
    
    当您看到以下值时,这就更有意义了:

      fmap . (*)
    = \x -> fmap (x *)
    
    因此,
    fmap-fmap(*)3
    只是
    fmap(3*)

    最后,您的
    test
    函数也有同样的问题:

    (.) fmap :: Functor g => (r -> x -> y) -> r -> g x -> g y
                             |___________|
                                   |
                                  (*) :: Num a => a -> a -> a
                                                  |    |    |
                                                  r -> x -> y
    
    test :: Functor f => f (a -> b) -> Bool
    
    将函子
    f
    专门化为
    (>)r
    ,我们得到:

    test :: (r -> a -> b) -> Bool
            |___________|
                  |
                (*3) :: Num x => x ->    x
                                 |       |
                                 |    ------
                                 |    |    |
                                 r -> a -> b
    
    因此,
    r:=x:=a->b
    。因此,我们得到类型签名:

    fmap . (*3) :: (Num (x -> y), Functor g) => (x -> y) -> g x -> g y
    
    fmap . (*) :: (Num a, Functor g) => a -> g a -> g a
    
    test (*3) :: Num (a -> b) => Bool
    
    由于输出类型中既不出现
    a
    也不出现
    b
    ,因此必须立即解决约束
    Num(a->b)
    。如果输出类型中出现了
    a
    b
    ,则可以对它们进行专门化,并可以选择
    Num(a->b)
    的不同实例。但是,由于它们不出现在输出类型中,编译器必须决定立即选择
    Num(a->b)
    的哪个实例;由于
    Num(a->b)
    是一个愚蠢的约束,没有任何实例,编译器会抛出一个错误


    如果您尝试
    test(*)
    ,那么您将不会得到任何错误,原因与我上面提到的相同。

    numa=>a->a
    f(x->y)
    对齐不好,您将得到
    f~(->)a
    a~x->y
    ,因此
    Num(x->y)
    。更有趣的可能是
    fmap($[1,2,3])$fmap-fmap$fmap(+)$Just 10
    ,它返回的
    仅为[11,12,13]
    。一个更有用的
    Functor
    组合器可能是
    fmap-fmap
    ,它允许您通过两个不同的
    Functor
    列出一个函数:
    (.:)=fm