如何理解Haskell中的函数"(.)(.)"

zha*_*nqi 6 haskell types

我是Haskell的初学者,我遇到了函数(.)(.),我用它:t来获取GHCi中的类型:

:t (.)(.)
(.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
Run Code Online (Sandbox Code Playgroud)

如何理解(.)(.) :: (a -> b -> c) -> a -> (a1 -> b) -> a1 -> c这里的类型?我很迷茫.

che*_*ner 4

这是组合运算符对组合运算符本身的部分应用。一般来说,我们知道如果我们应用(.)到某个函数f :: x -> y,那么

>>> :t (.) f
(.) f :: (a -> x) -> a -> y
Run Code Online (Sandbox Code Playgroud)

因为类型的排列方式:

(b -> c) -> (a -> b) -> a -> c
 x -> y
--------------------------------
            (a -> x) -> a -> y
Run Code Online (Sandbox Code Playgroud)

我们删除第一个参数,并将剩余出现的b和替换c为给定参数的相应类型。

这里,f又是(.),意味着我们识别x ~ (b -> c)y ~ (a -> b) -> a -> c。再次排列类型

(a ->   x   )  -> a ->              y
      b -> c               (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)

由于a出现在顶部和底部,我们需要为a底部选择一个新的变量名称;GHC 选择a1

(a ->   x   )  -> a ->               y
      b -> c               (a1 -> b) -> a1 -> c
Run Code Online (Sandbox Code Playgroud)

将两者放在一起会产生您在 GHCi 中看到的类型。

(a -> b -> c) -> a -> (a1 -> b) -> a1 -> c
Run Code Online (Sandbox Code Playgroud)

抛开解剖学笑话不谈,什么 (.)(.)

假设您有一个函数f :: a -> b,但您想要一个函数g :: a -> c,也就是说,您想要f但具有不同的返回类型。您唯一能做的就是找到一个辅助函数h :: b -> c来为您转换返回值。那么你的函数就是和g的组合:hf

g = h . f
Run Code Online (Sandbox Code Playgroud)

但是,您可能有一个更通用的函数h' :: t -> b -> c,它可以根据某些参数的值以多种方式将 type 的值转换b为 type 的值。然后你可以根据该参数得到很多不同的s 。cx :: tg

g = (h' x) . f
Run Code Online (Sandbox Code Playgroud)

现在,给定h'x、 和f,我们可以返回我们的g,所以让我们编写一个函数来执行此操作:给定一个函数和某个值,该函数将返回值f从 type 的值“提升”b为 type 的值:ch'x

promote h' x f = (h' x) . f
Run Code Online (Sandbox Code Playgroud)

您可以将任何函数机械地转换为无点形式;我不熟悉细节,但使用PointFree.io会产生

promote = ((.) .)
Run Code Online (Sandbox Code Playgroud)

这只是(.) (.)写成一个部分的部分应用程序,即:

((.) (.)) h' x f == (h' x) . f
Run Code Online (Sandbox Code Playgroud)

所以,我们的“胸部”运算符只是一个广义的预合成运算符。