Haskell - 类型和类型类
Haskell 是一种函数式语言,它是严格类型的,这意味着整个应用程序中使用的所有数据类型在编译时都会被编译器所知。
内置类型类
在 Haskell 中,每个语句都被视为一个数学表达式,这个表达式的类别称为 Type。可以说,“Type”就是编译时使用的表达式的类型。
要进一步了解 Type,我们可以使用 “:t” 命令。一般来说,Type 可以视为一个值,而 Type Class 可以视为一组相似类型的集合。本章中,我们将学习不同的内置类型。
Int
Int 是一个表示整数类型数据的类型类。范围在 2147483647 到 -2147483647 之间的每个整数都属于 Int 类型类。在以下示例中,函数 fType() 将按照其定义的类型进行行为。
fType :: Int -> Int -> Int fType x y = x*x + y*y main = print (fType 2 4)
在这里,我们将函数 fType() 的类型设置为 Int。该函数接受两个 Int 值并返回一个 Int 值。如果您编译并执行这段代码,它将产生以下输出 −
sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts sh-4.3$ main 20
Integer
Integer 可以视为 Int 的超集。这个值不受任何数字限制,因此 Integer 可以是任意长度的,没有任何限制。为了查看 Int 和 Integer 类型之间的基本区别,让我们修改上面的代码如下 −
fType :: Int -> Int -> Int fType x y = x*x + y*y main = print (fType 212124454 44545454454554545445454544545)
如果您编译上面的代码,将抛出以下错误消息 −
main.hs:3:31: Warning: Literal 44545454454554545445454544545 is out of the Int range - 9223372036854775808..9223372036854775807 Linking main ...
这个错误发生是因为我们的函数 fType() 期望一个 Int 类型值,而我们传入了一个非常大的 Int 类型值。为了避免这个错误,让我们将类型 “Int” 修改为 “Integer” 并观察区别。
fType :: Integer -> Integer -> Integer fType x y = x*x + y*y main = print (fType 212124454 4454545445455454545445445454544545)
现在,它将产生以下输出 −
sh-4.3$ main 1984297512562793395882644631364297686099210302577374055141
Float
查看以下代码片段。它展示了 Float 类型在 Haskell 中的工作方式 −
fType :: Float -> Float -> Float fType x y = x*x + y*y main = print (fType 2.5 3.8)
该函数接受两个 float 值作为输入,并产生另一个 float 值作为输出。当您编译并执行这段代码时,它将产生以下输出 −
sh-4.3$ main 20.689999
Double
Double 是一种双精度浮点数。查看以下示例 −
fType :: Double -> Double -> Double fType x y = x*x + y*y main = print (fType 2.56 3.81)
当您执行上面的代码片段时,它将生成以下输出 −
sh-4.3$ main 21.0697
Bool
Bool 是一个布尔类型。它可以是 True 或 False。执行以下代码以了解 Bool 类型在 Haskell 中的工作方式 −
main = do
let x = True
if x == False
then putStrLn "X matches with Bool Type"
else putStrLn "X is not a Bool Type"
在这里,我们定义了一个变量 “x” 为 Bool 类型,并将其与另一个布尔值进行比较以检查其真实性。它将产生以下输出 −
sh-4.3$ main X is not a Bool Type
Char
Char 表示字符。单引号内的任何内容都被视为字符。在以下代码中,我们修改了之前的 fType() 函数,使其接受 Char 值并返回 Char 值作为输出。
fType :: Char-> Char fType x = 'K' main = do let x = 'v' print (fType x)
上述代码将使用 char 值 'v' 调用 fType() 函数,但它返回另一个 char 值,即 'K'。以下是其输出 −
sh-4.3$ main 'K'
请注意,我们不会显式使用这些类型,因为 Haskell 足够智能,能够在声明之前推断类型。在本教程的后续章节中,我们将看到不同类型和 Type class 如何使 Haskell 成为一种强类型语言。
EQ Type Class
EQ type class 是一个接口,它提供了测试表达式相等性的功能。任何想要检查表达式相等性的 Type class 都应是这个 EQ Type Class 的一部分。
上面提到的所有标准 Type class 都是这个 EQ class 的一部分。每当我们使用上面提到的任何类型检查相等性时,实际上就是在调用 EQ type class。
在以下示例中,我们使用 “==” 或 “/= ” 操作内部调用 EQ Type。
main = do
if 8 /= 8
then putStrLn "The values are Equal"
else putStrLn "The values are not Equal"
它将产生以下输出 −
sh-4.3$ main The values are not Equal
Ord Type Class
Ord 是另一个接口类,它提供了排序功能。我们迄今为止使用的所有 types 都是这个 Ord 接口的一部分。与 EQ 接口类似,Ord 接口可以使用 “>”、“<”、“<=”、“>=”、“compare” 来调用。
请查看下面的示例,其中我们使用了这个 Type Class 的 compare 功能。
main = print (4 <= 2)
在这里,Haskell 编译器将检查 4 是否小于或等于 2。由于不是,代码将产生以下输出 −
sh-4.3$ main False
Show
Show 的功能是将它的参数打印为 String。无论参数是什么,它总是将结果打印为 String。在以下示例中,我们将使用这个接口打印整个列表。“show” 可用于调用这个接口。
main = print (show [1..10])
它将在控制台上产生以下输出。这里,双引号表示它是一个 String 类型的值。
sh-4.3$ main "[1,2,3,4,5,6,7,8,9,10]"
Read
Read 接口的功能与 Show 相同,但它不会将结果打印为 String 格式。在以下代码中,我们使用了 read 接口读取一个 string 值,并将其转换为 Int 值。
main = print (readInt "12") readInt :: String -> Int readInt = read
在这里,我们将一个 String 变量(“12”)传递给 readInt 方法,它在转换后返回 12(一个 Int 值)。以下是其输出 −
sh-4.3$ main 12
Enum
Enum 是另一种 Type class,它在 Haskell 中启用了顺序或有序功能。这个 Type class 可以通过诸如 Succ、Pred、Bool、Char 等命令访问。
以下代码展示了如何找到 12 的后继值。
main = print (succ 12)
它将产生以下输出 −
sh-4.3$ main 13
Bounded
所有具有上下界的类型都属于这个 Type Class。例如,Int 类型数据的最大界为 “9223372036854775807”,最小界为 “-9223372036854775808”。
以下代码展示了 Haskell 如何确定 Int 类型的最大和最小界。
main = do print (maxBound :: Int) print (minBound :: Int)
它将产生以下输出 −
sh-4.3$ main 9223372036854775807 -9223372036854775808
现在,尝试找到 Char、Float 和 Bool 类型的最大和最小界。
Num
这个 type class 用于数值运算。诸如 Int、Integer、Float 和 Double 等类型都属于这个 Type class。请查看以下代码 −
main = do print(2 :: Int) print(2 :: Float)
它将产生以下输出 −
sh-4.3$ main 2 2.0
Integral
Integral 可以被视为 Num Type Class 的子类。Num Type class 包含所有类型的数字,而 Integral type class 仅用于整数类型。Int 和 Integer 是这个 Type class 下的类型。
Floating
类似于 Integral,Floating 也是 Num Type class 的一部分,但它仅包含浮点数。因此,Float 和 Double 属于这个 type class。
Custom Type Class
与其他编程语言一样,Haskell 允许开发者定义用户自定义类型。在以下示例中,我们将创建一个用户自定义类型并使用它。
data Area = Circle Float Float Float surface :: Area -> Float surface (Circle _ _ r) = pi * r ^ 2 main = print (surface $ Circle 10 20 10 )
在这里,我们创建了一个名为 Area 的新类型。接下来,我们使用这个类型来计算圆的面积。在上面的示例中,"surface" 是一个函数,它以 Area 作为输入并产生 Float 作为输出。
请记住,"data" 在这里是一个关键字,Haskell 中的所有用户自定义类型都必须以大写字母开头。
它将产生以下输出 −
sh-4.3$ main 314.15927