先观察初始代码
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
main :: Effect Unit
main = log "hello"这里, 第一行是定义当前文件的模块, 下面三行import是引入其他文件定义的东西.
Prelude模块里有常用的类型和函数, 通常会将其全量引入.
Effect模块定义了关于副作用的相关类型和函数, 这里我们引入了Effect这个类型.
另外我们从Effect.Console模块引入了log函数, 这个函数能将信息输出到控制台上.
这个程序执行后, 会在控制台看到"hello".
如果改成这样:
main :: Effect Unit
main = log 1会报错, 因为log需要输入一个String, 而不是一个Int.
幸运的是有一个函数show可以将常见类型转换为Int, 类似于js里的toString, 但也有些不同, 之后我们会细说.
总之可以改成这样:
main :: Effect Unit
main = log (show 1)这个语言的任何地方都可以通过等量代换来重构, 例如, 上面的代码可以被写成:
x = "hello"
main :: Effect Unit
main = log x也可以:
x = log "hello"
main :: Effect Unit
main = x可以在值的上面一行写类型签名, 例如上面的代码可以写成:
x :: String
x = "hello"
main :: Effect Unit
main = log x类型是集合, 值是里面的元素.
常见的类型有, Int, String, Boolean等.
例如Boolean类型, 里面有两个元素, true和false.
而String和Int都有无限个元素, 是无限集合.
定义值的形式是
名称 :: 类型
名称 = 值a :: Int
a = 1
b :: String
b = "abc"
c :: Boolean
c = true
d :: Number
d = 1.0常见的复合类型有两种, 数组和记录.
数组是0个, 1个或多个同类型的值组成的有序集. 和常见的语言的数组类型没有区别.
arr :: Array Int
arr = [ 1, 2, 3 ]
arr2 :: Array String
arr2 = [ "1", "2", "3" ]记录是0个, 1个或多个键值对组成的无序集. 类似于常见的语言中的字典类型.
r1 :: { a :: Number, b :: String, c :: String }
r1 = { a: 1.0, b: "aaa", c: "bbb" }他们可以互相嵌套
r3 :: { a :: Number, b :: Array ({ x :: String }) }
r3 = { a: 1.0, b: [ { x: "aaa" }, { x: "bbb" } ] }函数是从一个值到另一个值的映射关系, 同时, 函数也是一种值.
例如
ff :: Boolean -> Int
ff true = 1
ff false = 0这定义了一个函数, 它的类型是Boolean -> Int, 即输入一个布尔值, 返回一个Int值.
它枚举了两种可能, 输入true时, 返回1, 输入false时, 返回0.
如果现在有
fx1 :: Int
fx1 = ff true注意到fx1 = ff true, 而观察ff的定义可以看到ff true = 1, 因此fx1 = 1.
例如
f3 :: Int -> Int
f3 ax = ax + 1现在有
fx3 :: Int
fx3 = f3 1其中, f3 ax称为模式, 其中ax会匹配任何值.
对f3 1而言, 可以被匹配成f3 ax, 其中ax = 1.
而对模式f3 ax而言, 等价于ax + 1, 因此f3 1 = 2.
例如
f5 :: Int -> Int
f5 0 = 1
f5 1 = 2这样会报错, 因为f5的类型签名写, 输入一个Int类型的值, 返回一个Int类型的值.
但定义f5的过程中, 只定义了0和1的模式, 如果输入其他值就搞不成了.
函数必须覆盖所有可能的输入值.
可以改成
f5 :: Int -> Int
f5 0 = 1
f5 1 = 2
f5 _ = 0其中, 下划线也是一种模式值, 类似上面的ax, 匹配任意值, 但不同的是, 下划线匹配的值不会被使用.
例如
f6 :: Int -> Int -> Int
f6 0 bx = 0 + bx
f6 ax bx = ax + bx这里f6有两个参数, 但我可以只传入一个参数来调用它.
fx7 :: Int -> Int
fx7 = f6 1其实并不存在"两个参数的函数", 所有函数都只有一个参数, 所谓的"两个参数的函数"只是"输入一个参数, 返回一个函数的函数".
这种特性叫函数的柯里化.
一个阶乘函数
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
jiechen :: Int -> Int
jiechen 0 = 1
jiechen n = n * (jiechen (n - 1))
main :: Effect Unit
main = log (show (jiechen 5)) -- 得到120