haskell 函数类型签名中的右结合性

8zzbczxx  于 5个月前  发布在  其他
关注(0)|答案(2)|浏览(55)

我很难理解Haskell中类型签名背后的推理。
1)当->被说成是右结合的,这是否意味着它可以用类似的方式来理解,例如4^(2^(3^2))?
2)使用简单函数的类型签名来表达我的怀疑(为了解释我的理解方式,我将使用abc而不是Num a => aInt):

myAdd :: a -> b -> c
myAdd x y = x+y

字符串
这意味着函数接受参数a并返回接受b并最终返回c的函数
但它可以重写为:

myAdd :: (a->(b->c))


正如大多数学习材料所述,c在我们的示例中是函数myAdd的结果,为什么根据括号的使用,它表明第一个“操作”是b->c?我如何从类型签名推断出执行操作的顺序?
3)我接到一个任务

map f xs


使用foldr(.)(:),结果是:

map f xs = foldr ((:) . f) [] xs


我没有问题理解上述函数的工作原理,但在这里我们又来了-类型签名。如果我们假设,名称是统一的,所以类型a在所有合同中表示相同的类型,似乎,cd可以用ab表示。在数学中,类似任务可能非常简单,但在Haskell中如何实现呢?

map :: (a -> b) -> [a] -> [b]
foldr :: (a -> c -> c) -> c -> [a] -> c
(:) :: b -> ([b] -> [b])
(.) :: (b -> d) -> (a -> b) -> a -> d

y0u0uwnf

y0u0uwnf1#

使用你的符号,在

myAdd :: a -> b -> c
myAdd x y = x+y

字符串
你正确地将类型解释为a -> (b->c),但你继续暗示b -> c中的计算以某种方式首先完成。
当计算myAdd 2 10这样的值时,函数的计算是从左到右的。
1)首先对myAdd 2进行求值。此求值的结果是将给定数字y发送到2 + y的函数。实际上,myAdd的定义与以下相同:

myAdd x = \y -> x+y


2)然后将最后一个函数应用于参数10,得到2 + 10 = 12
因此,->在 * 类型表达式 * 中的右结合性并不对应于 * 函数求值 * 中从右到左的计算顺序。事实上,函数求值是左结合的:myAdd 2 10(myAdd 2) 10相同。

xoshrz7s

xoshrz7s2#

在类型签名中,用“右结合性”这个术语来表示默认情况下不可见的括号是非常容易误导的。
在数学中,相同的符号在不同的领域和研究中表示不同的含义和用法。
显式或不可见的括号并不像算术优先级那样指示结合性。
在算术中,最里面的圆括号的计算首先计算最外面的圆括号。
而在Haskell的类型系统中,圆括号只表示另一个函数返回的函数,该函数在“->”符号之前接受前一个类型的参数,这是一个函数,该函数接受“->”符号之后的类型的参数,然后返回另一个函数,依此类推,直到某个函数返回最终结果。
例如:假设我们定义了一个函数,该函数接受三个类型参数,这些参数在类型类“”中被约束,并对所有给定的数字进行求和。

-- without explicit parentheses
sumThreeNums :: Num a => a -> a -> a -> a
sumThreeNums n1 n2 n3 = n1 + n2 + n3

-- with explicit parentheses
sumThreeNums :: Num a => (a -> (a -> (a -> a)))
sumThreeNums n1 n2 n3 = n1 + n2 + n3

字符串
当从ghci中的文件加载第二个带有显式括号的示例时,它被成功编译。
这里的类型推理“(a ->(a ->(a -> a))”表示函数“sumThreeNums”首先接受类型“a”的第一个参数并返回另一个函数,
它是“(a ->(a -> a))",它接受类型为“a”的第二个参数并返回另一个函数,
它是“(a -> a)",接受类型为“a”的第三个参数,并返回类型为“a”的最终结果。
在实践中,显式括号通常用于指示参数是一个函数,并且我上面展示的currying过程默认情况下省略了括号。
在这里,Happy Hacking:)

相关问题