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列表中有,

操作符

所有的操作符都是函数, 可以通过`来把前缀函数变为中缀, 如 7mod1, 7div` 2, 注意这两个函数都不能做中缀, 只能这样定义
, 注意相同优先级的操作符有左右连接的区分
:info 可以给出顺序

变量声明

变量声明的顺序无所谓, 加载时候会一次性全部加载, 但是REPL中就有关系了


module Learn where

x = 10 * 5 + y 

myResult = x * 5

y = 10

这个顺序是可以的, 记住module的名字是大写开头!

问题

  1. 缩进在haskell中是有效的, 所以不要使用tab!!
  2. 注意使用空格
  3. 注意上下对齐!
  4. 所有的声明必须在同一列上
  • 算数运算符

  • 语法糖
    用来使得语法更易读和易写
    如用-来表示negate
-- 无问题
1 + negate 100
-- 有问题
1 + -100
-- 无问题
1 + (-100)

括号中缀函数

  1. 可以用作值来表示
  2. 可以用作前缀函数, 如(+) 1 1, 但是+ 1 1 不行
  3. (1/) 2 (/1) 2相反
  4. 函数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

$符号