来自元组的函数 - Haskell

问题描述 投票:1回答:2

对于那些善于使用Haskell的人来说,这是一个简单的问题!为什么我写:

let a b = (5,6)

我获得了一个功能:

a :: p -> (a, b)

此外,b未实例化。我试图理解它是徒劳的。谢谢你的帮助!

haskell pattern-matching
2个回答
4
投票

答案更详细地说明了为什么Haskell会提出具体的结果:

好的,让我们分解吧。在Haskell中,数字常量可以有多种可能的类型,因此5可以是IntIntegerWord等,而(5,6)可能有(Int, Integer)(Int, Int)或其他类型。因此GHC任意地将其类型表示为(a,b),并将等待并查看您是否稍后在上下文中使用该结果,以便推断该类型。如果没有,它将默认为(Integer, Integer)

碰巧GHC为在等号左侧用作标识符的未知类型分配了相同的小写字母,但它们是相同名称的无关使用。您可以将a b =更改为f _ = (5,6),如果这样可以更清楚地了解Haskell在字面意义上所看到的内容。

你的声明a b是一个名为a的函数,有一个参数b。此参数未使用,因此除非您调用它,否则GHC无法推断出它的类型,并将其指定为p

所以你声明了一个函数,它接受一个p并返回一对未知类型的数字abp -> (a,b)

正如Sivio Mayolo打败我说的,如果你想绑定变量ab,你可以写(a,b) = (5,6)

P.S.:

HTNW建议我多谈一下这些数字类型的价值。 The Haskell 2010 Report说,“整数字面表示函数fromInteger应用于Integer类型的适当值。”

所以,常数5相当于(fromInteger (5::Integer))

fromInteger定义在哪里?作为类型类Num的一部分。它有fromInteger :: (Num a) => Integer -> a类型。也就是说,类型instance(包括NumInt等)的每个Double都有自己定义的fromInteger多态版本,它将Integer转换为该类型的值。

如果编译器可以推断出常量的类型,或者因为你提供了类型注释,或者因为你在需要特定类型的表达式中使用它,那么它将知道调用哪个版本的fromInteger来获得该类型的常量。 (实际上,它肯定会在编译时优化掉调用,并且只是在运行时使用结果。)如果你完全放弃编译器,GHC将选择Integer并给你一个警告。

如果你试图绑定x = 5然后使用x作为任何类型的Num,它将工作 - 但如果你尝试将它用作两种不同的类型,编译器将无法解决它。所以,如果你需要在一行中使用x作为Int,然后你想稍后将其作为Double传递,你需要将转换为Double写为(fromIntegral x)。这样,x一直是Int,每个人都很开心。

类型系统可能但不允许的转换示例:与C不同,尝试将数字文字用作Char将不起作用,因为Char不是Num的实例。然而,它是一个Enum,所以你可以使用fromEnumtoEnumCharInt之间进行转换。

所以,如果你在GHCI中尝试这个,你得到:

Prelude> 7 :: Char

<interactive>:1:1: error:
    • No instance for (Num Char) arising from the literal ‘7’
    • In the expression: 7 :: Char
      In an equation for ‘it’: it = 7 :: Char

Char不是Num的一个例子,因此没有版本的fromInteger转换为Char

Prelude> toEnum 7 :: Char
'\a'

这实际上是toEnum (fromInteger 7) :: Char。每个Enum都有toEnum函数,从Int转换为Enum。最后的:: Char告诉GHCI转换的EnumChar,所以GHCI可以推断它应该从Integer转换为IntChar。 Haskell从C借用了ASCII / Unicode代码点7的'\a'转义码。(对于警报,如果你在终端上键入control-G,它曾经发出嘟嘟声。它可能仍在你的Linux控制台中工作,或至少制作窗口闪烁。)


8
投票
let a b = (5, 6)

您正在定义一个名为a的函数,它接受一个名为b的参数。此函数忽略其参数并返回(5, 6)。我假设您希望ab分别绑定到56,在这种情况下,您需要以下语法。

let (a, b) = (5, 6)
© www.soinside.com 2019 - 2024. All rights reserved.