Haskell的味道:对Haskell的定义。 (组成)

时间:2014-04-03 15:01:30

标签: haskell

在52 {40 this讲话中讨论了以下幻灯片。

我不太明白这个定义。

为什么定义中需要x

为什么不需要输出?

例如,为什么定义不是(f . g) x -> y = f (g x) -> y或类似的东西?

如果我将其视为重写规则,是否更容易理解此定义?每当表达式求值程序遇到左侧的模式时,它应该将其重写到右侧吗?

重写规则解释是理解这个定义的唯一正确方法吗? (这种解释是这个定义对有意义的唯一方法,但我甚至不确定这是否是解释这个定义的正确方法。)我不得不承认我是一个初学者Haskell中。

编辑:

或者这个定义只是lambda表达式的糖?

或者lambda表达式是重写规则的糖?

enter image description here

4 个答案:

答案 0 :(得分:7)

作文可以用几种等效的方式书写。

(.) = \f g -> \x -> f (g x)
(.) = \f g x -> f (g x)
f . g = \x -> f (g x)

最后一个例子说"两个函数的组合给出了一个函数,这样......"

更多等效表达式:

(.) f g = \x -> f (g x)
(.) f g x = f (g x)
(f . g) x = f (g x)

答案 1 :(得分:4)

也许中缀符号让你感到困惑?让我们看一下编写该定义的另一种方式:

(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)     -- definition 1

因此,我们可以将(.)视为一个函数,它接受三个参数f(一个函数),g(另一个函数)和x(一个值) )。然后它返回f ( g x)。要调用此函数,我们可以编写如下表达式:

(.) head tail "test" 

将返回'e'。但是,名称以某些特殊字符开头的函数(如.)可以使用 infix 样式,如下所示:

(head . tail) "test"

现在,另一种定义.的方法是这样的:

(f . g) x = f (g x)       -- definition 2

这与"定义1"相同,但符号可能看起来有点奇怪,功能" name" .出现在第一个参数之后!但这只是中缀符号的另一个例子。

现在,让我们看一下Prelude中实际定义此函数的方式:

(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

这相当于定义1和2,但使用了您可能不熟悉的语法, lambda表示法。右侧引入了一个带有单个参数x的未命名函数,并将该函数定义为f (g x)。所以整个事情说明函数(.) f g被定义为未命名的函数!你可能想知道为什么有人会以这种奇怪的方式写这个。当你使用中缀符号和lambdas一段时间时,它会更有意义。

答案 2 :(得分:2)

函数的类型定义需要

->,即说明函数作为参数使用的类型以及函数的结果类型。说明:

  • f :: ...类似于“功能f类型...
  • (a -> b)表示类型“带有类型a的参数并返回类型为b的参数的函数”
  • (a -> b) -> (b->c) -> (a->c)表示“一个函数,它接受a->b类型的函数和类型b->c的函数,并返回类型a->c的函数”(这是一个简化的配方。请参阅下面的注释)

第二行是f.g的定义。它就像在数学中定义函数一样。在那里,您可以通过说明任何给定参数h h(x)的结果来定义函数x(例如,您可以编写h(x)=x²)。您已阅读

(f . g) x = f (g x)

作为

(f . g)(x) := f(g(x))

应理解为:“任何给定f . g的函数x的结果应为f(g(x))

结论: ->就像数学中的箭头一样,您可能会在f : R -> R=这样的术语中看到:=f(x):=x²表示在数学中,f(x)被定义为

注意: (a -> b) -> (b->c) -> (a->c)的实际类型(如@Ben Voigt所述):“函数采用类型a-> b的函数并返回映射函数类型b-> c的函数在a-> c“类型的ta函数上。 @jhegedus:如果您需要解释,请在评论中注明。

答案 3 :(得分:2)

这不是(。)的源代码,虽然它很接近。这是实际来源:

-- | Function composition.
{-# INLINE (.) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

(f.g)是一个函数。源使用lambda形式作为该函数。 lambda表单必须为其每个参数提供本地绑定。在这种情况下,只有一个参数,它在本地绑定到名称“x”。这就是为什么必须提到'x'(类型'a')的原因。

由于它标记为INLINE,它将在优化器传递期间有效地重写代码。 (IIRC,这是在desugaring(转换为Core)之后和转换为STG之前。)

Lambda不是糖,它们是基础。让/什么是lambdas的糖。

lambdas的函数定义几乎糖,但优化器(在GHC中最少)使用定义中的arity来确定何时/如何内联函数。类型“(b - > c) - >(a - > b) - > a - > c”可以被认为是从0到3的任何arity,并且可以用任何这些arities定义

不必要的括号可用于强烈暗示你想要使用的arity,尽管幻灯片从我见过的惯例向后倾斜。例如,在“a - > c”周围添加括号以得到“(b - > c) - >(a - > b) - >(a - > c)”;该类型通常被认为是二进制函数类型。幻灯片使用该类型,但随后使用三元定义。