动机
无意中看到的知乎推荐,了解到了这门课,觉得自己经常忙于学习新东西新框架,应该多去关注下那种普适性的知识,比如数据结构,语言设计什么的,所谓的基础知识。扫了下课程的大致内容里面提到了high-order-function, type inference等前端最近经常提到的概念瞬间就有了兴趣呢^_^
课程简介
这门课聚焦在不同语言的通用核心概念上,可以帮忙更好的理解现在使用的语言以及掌握新的语言。当我们谈及语言时通长避不开下面5个方面
- syntax语法。语言规定怎么写的,比如在ml里布尔值连接是
andalso其他很多语言是&& - semantics语义。就是概念类的,语言里的各个特性的含义。syntax和semantics的区别 - jiyanfeng1的专栏 - CSDN博客syntax是句法,semantics是语义,不同语言针对相同的语义有不同的句法。讲义上提到了type checking rules 和evaluation rules,它们也算语义方面的嘛。我理解的是比如说’加法‘这是个语义。所以我个人感觉这里的应该是每个语言都有type checking 的语义和evaluate的语义这两个概念,只是规则不同。
- idioms. 风格。语言特性的组合常规使用模式。组合使用语言里的特性结果某类特定问题,比如在sml里nest function 就是利用了let expression的特性。
- libraries 库。访问系统文件类的语言不提供给你你就没法自己做的;还有就是那种类似util类的函数可以自己实现。
- tools 工具。REPL debugger 这些都不是语言的一部分,只是某个语言的具体实现为了让你工作更顺畅所提供给你的功能。比如debugger在javascript中是浏览器给了它调试的功能。
这门课聚焦在semantics和idioms上
本门课的编程语言以及工具
EMACS
教授说是不用太多时间去设置环境,但是很多快捷键很难记,其实现在vscode很好用了也是开箱即用。。。最主要的原因还是因为课程里用的是这个,为了和教授保持一致还是用吧o(╯□╰)o
ML, Racket, Ruby
这门课三部分,每个部分使用不同的编程语言进行教学。两个函数式语言一个面向对象语言,语言本身不重要,重要的是了解语言是怎么将一系列概念组合起来的,以及一些编程范式等。
ML表达式类型和值
环境
ML程序是一系列的绑定binding. 每个绑定都要进行类型检查type-checked, 如果类型检查通过后再进行求值(evaluate). 每个绑定的类型是由静态环境(static enviroment)决定的,里面存放了一些列的绑定的类型。绑定的值由由动态环境(dynamic environment)决定的,里面存放了之前一系列绑定的值。静态环境有个同义词叫上下文(context)。e.g. 值不存在的报错是由静态环境告知的
表达式expression
值value本身也是表达式,value本身没有更多的计算去做,相当于原子那样无法再精简。8+9就是一个表达式,表达式需要被求值
变量 variable binding
val x = e;
类型检查和取值都取决于表达式e。利用静态环境根据之前一系列绑定得出现在的e是个什么类型,创建了一个新的静态环境,求值类似。另外ml里不存在assignment statement操作。上面的代码中x和e的值是绑定上了。如果再定义val x = e2就创造了新的环境。个人理解是创造了新的x和e2的绑定,网上说可以理解成栈那样。
加法运算 addition
e1+e2
类型:表达式e1 e2 必须是int类型,否则does not type-check.
求值:在静态环境里分别求值e1 e2,再求他们的和
减法运算类似。负数用~表示,-只能用于两数相减
条件 conditionals
if e1 then e2 else e3
类型:e1 需要是布尔。e2, e3 需要是同样的类型。上面整个代码块也是一个表达式,整个表达式的类型就是e2或者e3的类型。
值:根据e1求值的结果决定整个表达式的值是e2还是e3
布尔 boolean
true or false这两个写法中的一个
类型:布尔
值:它自己本身
小于比较 less-than comparison
e1 < e2
类型:如果e1, e2都在静态环境里计算出是数字类型的话则表达式的类型是布尔,否则does not type-check
值:求值e1和e2根据结果是true或者false
函数定义 function definition
不像其他语言没有类概念,没有this
fun x0 (x1 : t1, …, xn : tn) = e
函数的类型表示是 t1* …* tn -> t。top-level的是在一个静态环境内,函数在另一个静态环境里,这个静态除了包含top-level的静态环境还包含x1,xn以及x0用于递归调用
类型:在不同于top-level的静态环境中检查x1, x2… e的类型就是整个函数表达式的类型。这个类型是由type-checker推导出来的。(type inference)
值:函数本身就是value,不能再被精简了。
函数调用 function call
e0(e1,...en)
类型:会检查参数的类型以及参数数量,整个表达式的参数就是之前函数定义时的 -> t
值:先求e1..en的值再函数运算后的值
pairs and other tuples
compound data 复合数据
(e1,e2)
类型:t1*t2
值:(e1,e2)本身就是一个value
可以用#1 #2来取得e1,e2的值。
pairs 可以理解为特殊的tuples,tuples可以嵌套。
((7,true),9) 的type是(int * bool) * int
列表 lists
tuples的所有元素可以是不同类型,但是要指定元素数量; lists的所有元素类型必须一致,但是可以不指定元素数量; 类型:int list , bool list ….. [] 的类型是 ‘a list 表示任何类型 值:列表本身就是value
let expression
let b1 b2 … bn in e end
用于本地绑定。和局部变量很像,一个方法只用于另一个方法内的情况也可以这么使用。good style
b1, b2, b3 …都是绑定。函数也是一种绑定~
每个绑定的可使用范围都是在之后的绑定以及主体e中。和top-level的绑定的按顺序很像。
类型:e的type就是整个let表达式的值。
值:e的值
options
表示值的取值可以使空的或者有值。这里的空不一定值数组是空的就是表示carrying nothing.这里感觉就是1 0 两种取值。可以理解成某个容器里面有值和没值吧。
NONE 表示没值
SOME 将值转化为option,通过 valOf 取出
isSome 方法,参数是option,如果是true表示有值。
valOf 方法,参数是option, 返回值,如果是空值(NONE)则报错。下面的例子是用于求和的。
fun better_max (xs : int list) =
if null xs
then NONE
else
let val tl_ans = better_max(tl xs)
in if isSome tl_ans andalso valOf tl_ans > hd xs
then tl_ans
else SOME (hd xs)
end
expressions and operators
其他有些特殊操作符构成的表达式
andalso
e1 andalso e2
类型:e1 e2 必须都是布尔
值:整个表达式也是布尔,如果e1是false就直接中断
比if e1 then e2 else false的style更好。这个形式利用现有语法当然可以写,但ml提供了更简洁的方式
short-circuiting
orelse
e1 orelse e2
类似上面的只不过是或
比if e1 then true else e2
not
not e
类型: e是布尔
值:取反
如果自己实现一个方法则类似
fun not x = if x then false else true
not 是方法,andalso和orelse是操作符,因为方法会对参数都进行取值,而andalso和orelse有中断操作。
no mutation
特性
在ML中,绑定过得数据无法更改,tuple和list内的值也无法改变。假设
val x = value1;x在其所属的环境中值不变。如果有新的绑定,在新环境中x就是对应的新值;在原来的环境中x还是value1。环境的链接个人感觉是不是和list 的::操作符一样,和栈一样。 ml中的列表的tl就是返回的引用,时间复杂度是常数级。
优点
- 不用担心一个绑定是否是是引用(aliases)。 举自己熟悉的js为例,引用类型是要小心的点,采用引用类型的时候担心其他使用引用的地方会受到影响。
- 节省空间 比如js中为了不造成影响会创建新的对象。redux中推荐的做法就是创造no mutation的数据;就是构造新的引用类型,不返回旧的
- 安全问题
class ProtectedResource {
private Resource theResource = ...;
private String[] allowedUsers = ...;
public String[] getAllowedUsers() {
return allowedUsers;
}
public String currentUser() { ... }
public void useTheResource() {
for(int i=0; i < allowedUsers.length; i++) {
if(currentUser().equals(allowedUsers[i])) {
... // access allowed: use it
return; }
}
throw new IllegalAccessException();
}
}
由于常用,java中有专门System.arraycopy来做copy数组的事情。
结尾
ps: 有一起学的小伙伴可以找我哈(^o^)/~~