Haskell Programming from first principles
Haskell Programming from first principles 这本书笔记
第一章 lambda calculus
lambda
${\lambda}x.x$
lambda基础结构如下, 注意的是lambda抽象是匿名函数,
- alpha equivalence
alpha等效, 即${\lambda}x.x$与${\lambda}y.y$等效, 注意这里的等效只对$\lambda$后面的 bound variable 有效, 对free variable 无效,如${\lambda}x,xy$中的y是free variable
Beta reduction
用参数消去lambda抽象的头, 为Beta reduction
如$({\lambda}$$x.x)2$
过程为$[x:=2]$, 这里的$:=$为Beta reduction, 即函数赋值, 注意这里的参数可以为另一个lambda抽象, 不一定只能是变量
Multiple arguments
可以把多参lambda抽象转换成多个单参lambda的组合
Evaluation is simplification
Combinators
没有free variable 的 lambda抽象为combinator
不需要插入除了参数外的外部变量
Divergence
Beta reduction 也许不会终止, 即不收敛, 那么就会发散, 编程时需要避免发散, 否则无法返回有用的结果, 这里在递归使用的时候非常重要.
总结
函数编程需要从lambda抽象的角度来思考问题, 定义一个函数时要注意收敛性(注意一个函数只能接受一个变量, 多变量函数是单变量函数叠加起来的)
函数即是映射
haskell 是 typed lambda calculus, 语法的核心与lambada积分一致, 即, haskell程序主要在解析表达式而不是在执行指令,
习题
- Beta reduction
本章定义
- lambda 本身为符号${\lambda}$ 用来引入, 或抽象参数以限制表达式
- lambda抽象为匿名函数, 主语为抽象
- lambda积分为一种用抽象与应用来表达程序的系统
第二章 hello, Haskell!
sayHello :: String -> IO()
sayHello x = putStrLn "Hello, " ++
x ++ "!")
:l :q :r command
这里的::表示类型签名. has a type
可以看到是putStrLn返回一个IO类型
表达式
一个表达式必须一直约化为不可约的状态.即 Normal form
函数
函数是一种特殊的表达式, haskell里的与lambda积分的函数一致, 只有一个输入与一个输出, 多变量函数实际上是单变量函数的嵌套定义
定义函数
在ghci与在源文件里定义函数不同的是, 在ghci里定义需要加上let
argument 与 parameter的区别
argument更多的代表传递给函数的值, 而parameter更多表示定义的变量
变量名
一般来说函数名与普通变量名小写开头, 模块和类型名字用大写开头, 比如Integer
x,xs列表中有,
操作符
所有的操作符都是函数, 可以通过`来把前缀函数变为中缀, 如 7
mod1, 7
div` 2, 注意这两个函数都不能做中缀, 只能这样定义
, 注意相同优先级的操作符有左右连接的区分
:info 可以给出顺序
变量声明
变量声明的顺序无所谓, 加载时候会一次性全部加载, 但是REPL中就有关系了
如
module Learn where
x = 10 * 5 + y
myResult = x * 5
y = 10
这个顺序是可以的, 记住module的名字是大写开头!
问题
- 缩进在haskell中是有效的, 所以不要使用tab!!
- 注意使用空格
- 注意上下对齐!
- 所有的声明必须在同一列上
- 算数运算符
- 语法糖
用来使得语法更易读和易写
如用-来表示negate
-- 无问题
1 + negate 100
-- 有问题
1 + -100
-- 无问题
1 + (-100)
括号中缀函数
- 可以用作值来表示
- 可以用作前缀函数, 如(+) 1 1, 但是+ 1 1 不行
- (1/) 2 (/1) 2相反
- 函数sectioning let y = (1 -)
商与余数
(quot x y)y + (rem x y) == x
(div x y)y + (mod x y) == x
where 和 let
这两个关键字用来引入表达式的名
module FunctionWithWhere where
printInc n = print plusTwo
where plusTwo = n + 2
--also
module FunctionWithLet where
printInc2 n = let plusTwo = n + 2
in print plusTwo
此为let表达式
相当于mathematica的module, 定义变量后in the modules
用:load装载的坏处是不能载入多个module, 所以需要使用cabal和cabal repl来管理而不是ghci
desugaring let to lambda
对let进行脱糖(有证明对let,很容易可以转谎称简单的lambda形式)
如
printInc2 n = let plusTwo = n + 2 in print plusTwo
-- turns into
printInc2' n = (\plusTwo -> print plusTwo) (n + 2)
所谓转换成lambda, 就是转换成匿名函数的形式, 具体为把前面的赋值过程换到匿名函数的赋值部分, 后面的抽象部分则直接转换成lambda匿名函数的形式
let in 与 where是正好相反的过程
可以给lambda函数命名, 如let id = \x -> x