函数式语言:
F#是ML的一个变种,在.net平台上运行; 其他的还有Haskell, OCaml
ML:static functional language
函数编程:1. immutable data; 2. function as value
ML是一系列的binding,如果一个binding type-checked(类型检查通过)就evaluate(求值)
binding的类型取决于static environment(context)------type-check
binding的值取决于dynamic environment(environment)------evaluate
syntax: 语法,如何写的
semantics: 语义,如何type-check and evaluate [通常对expression e]
type-check: 使用当前的静态环境检查e,并且生成一个新的静态环境
evaluate: 使用当前的动态环境求值e,得到value v,并且生成一个新的动态环境
值是immutable的:在当前环境中,x一定是某个值的。
没有赋值语句,只会创建新的环境,使得x shadow了之前定义的值。
优点:使得alias无关紧要了,即使底层实现是创建拷贝还是使用相同的内存,在修改时一定会指向不同内存
新类型的种类:each-of(类似class), one-of(类似union, enum), self-reference(以便创建递归的类似list的结构)
tail recursion + accumulators: 尾递归
基本思路:创建helper函数,参数加上acc。递归中的运算变成参数的运算
原理:所有没有执行完成的父函数都会放入递归栈中,直到子函数调用完成。而在尾递归的情况下,由于在调用子函数后没有其他事情要做了,所以会将父函数先推出栈,再将子函数放入栈中。
tail position:if e1 then e2 else e3 :e2和e3是
函数体是
let b ... in e end: e是
函数调用的参数不是
first-class function: 函数能进行计算、传值、排序
作为参数传入(anonymous function or function name)
map: ('a -> 'b) * 'a list -> 'b list
implementer(map函数)直到如何便利list,client(使用map的人)知道如何处理数据
filter
fold
函数参数f的构造部分封装在另外一个函数里,创造出对于f的closure
返回函数: t1 -> (t2 -> t3),括号不需要,因为->是关联右侧的,t1->t2->t3 = t1 -> (t2 -> t3)
function closures:函数使用外部定义的变量
函数体的求值是根据定义时的上下文(lexical scope),而非调用时的上下文(dynamic scope)
函数有两个部分:代码+上下文,即函数闭包closure。意义是传入参数后,函数能根据已有的binding得到结果
lexical scope: 1. 函数的定义和lexical scope的变量名无关----可以重命名变量,也可移除没有用到的变量
2. 函数在定义的时候就能进行类型检查
3. 闭包能存储需要的数据
function composition:将函数串联,一个函数的输入是另一个函数的输出 f(g(x))。如果函数的顺序是从左到右的就是一个pipeline: x g f
currying: 定义一个函数,使用第一个参数并返回一个函数使用第二个参数。。。closure在这里起到作用,返回的函数里的上下文中有第一个参数
var f = fn x => fn y => x > y 或者 fun f x => fn y => x > y , 语法糖 fun f x y = x > y
调用: (f 1) 2, 也可以写成f 1 2
partial application:只填部分的参数,返回一个需要剩余参数的函数
callback: 当event发生时,调用所有event对应的函数
ADT: 同样环境中的closure就像object,函数是成员方法,variable binding是成员变量
high-order function: 函数的返回值是其他的函数
代码管理:模块化(structure&signature)
接口和实现分离
signature没有给出的接口,外部不能用
模块有invariants:仅内部可见的,和函数的实现有关的。所有函数都默认的假设,并且保证为真的假设
模块有properties: 外部可见的,是保证给客户端的
1.不要把datatype binding暴露
2.把可以暴露的datatype的constructor作为函数暴露
静态类型语言:Java, C, ML...
类型在编译期确定,由type-checker决定是否通过编译
和Java不同,ML是implicitly typed,极少需要指定类型。需要类型推断和类型检查
类型推断(type inference):查看类型
类型检查(type checking):决定是否匹配
动态类型语言:类型在运行时确定,如果类型错误会导致运行错误
类型推断:1.依次判断binding的类型
2.获取binding类型的信息,决定可以知道的类型
3.对于不能知道的类型,使用type variable(‘a...)
除非没有给足类型信息(如#1,编译器不知道一个tuple或record有几个域,域名是什么),否则不会被reject
value restriction: ML会给一个变量多态类型(‘a)仅仅出现在binding是一个值或者一个变量,如果是一个函数则不行。ref是一个函数。这样会给变量dummy type而非变量类型。除非使用type annotation给变量一个非多态类型,如int option ref NONE
Equivalence of functions:
1.产生相同结果
2.有相同的结束条件
3.改变相同的数据(副作用)
4.有相同的输入、输出(副作用)
5.抛出相同的异常
肯定相同的情况:语法糖、local变量名改变、不适用helper function
use: add bindings from other file
andalso, orelse, not:逻辑运算
=,<>,<,>逻辑运算
~:negate
type t0 = t1: 别名。t0是t1的别名,t0和t1是可以相互替换的。datatype binding则引入了一个新的类型,和已有的任何类型不同
'a: 多态类型,可以用任何类型替代,但是一定是一致的,即所有的'a都变成int,所有的'b都变成string...
''a:可以比较的多态类型
exception:
定义:exception E1 of int, exception E2
typing: E1: int->exn, E2:exn, E1 23:exn
使用:raise E1 23, raise E2
处理:e1 handle p => e2。p是pattern。使用dynamic call stack查找handle expression
reference:内容可变的 ref e
读:!r
写:r := e
structure: structure name struct bindings
封装binding到模块中去(namespace management),通过Structure名.binding名访问
也可以open structure,直接就可以通过binding名访问
signature: 规定structure的interface,structure可以使用signature,这样就必须定义signature中对应的bindings
and:用于datatype binding和function binding中,定义相互引用的两种类型(f调用g,g调用f)