programming-languages学习笔记–第7部分

*/-->

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

programming-languages学习笔记–第7部分

1 ML对比Racket

有很多共同点,也有一些不同点,最大的不同是ML的类型系统。

类型检查的优点与缺点。
ML在程序运行前通过类型检查拒绝程序并报告错误。
ML通过在编译时强制一些规则来确保没有某些错误(比如不能传递一个字符串给+)。

从Racket的观点看ML:

  • 语法上,ML是Racket的一个子集:对于产生同样结果的程序,ML会认为很多程序不合法。
    优点是ML会拒绝看上去像是错误的程序。
  • 许多程序不允许有bug,比如:
(define (g x) (+ x x)) ;类型检查确保正确。
  • 实际上,ML中不需要number?这样的判断
  • 一些程序无法实现,比如:
(define (f x) (if (> x ) #t (list  )))
(define xs (list #t "hi"))

从ML的观点看Racket:

  • 可以认为Racet有一个巨大的datatype,所有的值都是这个类型:
datatype theType = Int of int | String of string
| Pair of theType * the Type
| Fun of theType -> theType
| ...
  • 构造器是隐式的(值被标记)

    • 42就是Int 42
  • primitives隐式检查标记并提取数据,对错误的构造器抛出异常

theType内置的构造器:numbers,strings,booleans,pair,symbols,procedures,等

struct-definition创建一个新的构造器,动态添加到theType.(ML中不允许,但是异常exn与此类似)

2 静态检查

静态检查在程序parse之后运行之前进行,发现其中的错误。语法解析过程中的错误叫做语法错误,静态检查的错误叫做类型错误。

执行静态检查是语言定义的一部分。
常用的做法是通过类型系统进行静态检查。比如ML中每个语言构造的类型规则。
静态检查的目的是拒绝无意义或可能试图滥用语言功能的程序。
作为对比,racket使用动态检查(运行时检查)发现错误,为不同类型的值打上标记(tag),在函数应用时检查是否符合规则。

静态检查会拒绝一些不会产生任何错误的程序。

类型系统无法防止逻辑/算法错误。

类型系统麻烦的的部分在于确定类型检查确实达到目的。

静态检查/动态检查是整个流程中的两个点,编译时和运行时。
比如要防止3/0的错误,可以在以下几个方法入手:

  • 键入时: 编辑器中不允许
  • 编译时:代码中不允许出现
  • 链接时: 不允许执行调用
  • 运行时: 当进行除法时禁止
  • 之后处理: 代替除法,直接返回+inf.0

3 正确性:可靠性,完整性,不确定性

使用精确的术语描述类型系统的正确性,比如要防止一些事情X:

  • 可靠性:当类型系统从不允许一个程序使用某些输入时执行X操作,称它为可靠的
  • 完整性:当类型系统从不拒绝一个不管什么输入都不会执行X操作的程序,就是完整的。

可以认为可靠性防止漏报,完整性防止误报。

可靠性很重要,因为它让语言用户和语言实现者可以依赖X永远不会发生。

ML的类型检查器就是不完整的,有些正常的程序无法通过检查,比如:
if true then 3 else "hi"

不确定性是计算理论研究的核心。

4 弱类型

weak typing:程序必须通过静态检查,但是运行时没有任何限制。
c/c++,最常见的就是数组越界访问。

Racket是动态类型,运行时有类型检查,不是弱类型。

5 静态类型vs动态类型

静态类型在早期发现很多错误,可靠性保证一些类型的错误不会发生,不完整性意味着一些完美的程序被拒绝。

5.1 静态类型或动态类型哪个更方便?

动态类型的优点是可以混合和匹配不同类型的数据,而不需要声明新的类型定义或使用模式匹配。

另一方面,静态类型可以假定数据有正确的类型,不需要额外的代码进行检查:

(define (cube x)
(if (not (number? x))
(error "bad arguments")
(* x x x)))
(cube )
fun cube x = x * x * x
cube

5.2 静态类型是否限制一些有用的程序?

静态类型不能实现一些程序,比如:

(define (f g) (cons (g ) (g #t)))
(define pair_of_pairs (f (lambda (x) (cons x x))))
fun f g = (g , g true) (*无法通过类型检查*)
val pair_of_pairs = f (fn x => (x,x))

静态类型可以按照需要进行tag,使用datatype,实践中很少需要,(Racket总是包含tag):

datatype tort = Int of int
| String of string
| Cons of tort * tort
| Fun of tort -> tort
...
if e1
then Fun (fn x => case x of Int i => Int (i * i *i))
else Cons (Int , String "hi")

支持静态类型的论点是现代类型系统具有足够的表达能力,很少会妨碍你的工作。

5.3 静态类型的早期错误检测重要吗?

因为静态类型检查可以捕获已知的错误类型,所以可以使用这些知识将注意力集中到其他地方。

(define (pow x)
(lambda (y)
(if (= y ) (* x (pow x (- y ))))))
fun pow x y = (* 无法通过类型检查 *)
if y =
then
else x * pow (x, (y - ))

动态类型的支持者会说静态类型检查只能发现测试中可以捕获的bug,对于语义错误无法发现。

5.4 动态类型还是静态类型能带来更好的性能?

  • 静态类型更快,语言实现:

    • 不需要存储tags(空间,时间)
    • 不需要在运行时检查tags进行类型测试(时间)
  • 动态类型反对意见:
    • 在大多数软件中,这种低水平的性能并不重要。
    • 语言实现可以优化移除不必要的tags和测试
    • 如果在静态类型系统中要突破类型系统的限制,使用变通方法也会影响性能优势

5.5 静态还是动态类型更容易代码重用?

动态类型更容易重用库函数,如果使用cons构造不同的数据,只要使用car,cdr,cadr等就可以访问,不需要为不同的数据类型定义不同的访问器函数。

静态类型的观点:

  • 现代类型系统通过generics和subtyping等特性支持代码重用
  • 如果使用cons表示所有东西,会被表示的东西弄混,并且很难调试错误。
    • 使用单独的静态类型保持思想独立
    • 静态类型可以避免库的误用

5.6 静态类型和动态类型哪个更适合原型开发

在软件项目的早期,你会开发一个原型,与此同时,你将改变对软件做什么和用什么方法实现的看法。

动态类型更容易实现原型(prototyping),在早期,你可能不知道需要什么数据类型和函数,因此不用去定义。有一部分程序还没有实现,可以测试已经写好的部分,动态类型可以让不完整的程序运行。
静态类型不允许这样的代码,,因此过早地对数据结构作出承诺,然后编写以后会扔掉的代码用来通过类型检查器 ,是令人沮丧的原型设计。

静态类型的反对观点是,静态类型更容易原型设计,使用类型系统能更好地记录你不断变化的数据结构和代码用例的决策:
新的、不断演化的代码最有可能作出不一致的假设。

5.7 静态类型或动态类型更适合于代码演进吗?

软件工程中的很多工作都花在维护工作上,修复bug、添加新特性,以及对代码进行修改。

动态类型更容易演化改进,修改代码不会影响旧的调用者,比如接受一个int或string代替一个int:
Racket调用者不用做任何修改

(define (f x) (*  x))
(define (f x)
(if (number? x)
(* x)
(string-append x x)))

ML调用者修改后必须在参数上使用构造器,并对结果使用模式匹配。

fun f x =  * x
fun f x =
case f x of
Int i => Int ( * i)
| String s => String (s ^ s)

另一方面,静态类型检查对于捕获演进过程中引入的bug很有用,修改数据或函数的类型,类型检查器会给我们一个所有要修改的地方的"to do" list:

  • 避免引入bug
  • 类型中的规范越多,类型检查器列出的类型更改时要更改的内容就越多。
  • 反方:todo list是强制性的,这导致改进过程很痛苦:不能以部分的方式进行测试。

开发项目中的现实:

  • 在实现稳定前经常需要很多原型
  • 在1.0版之后会有很多维护/演化

静态类型对比动态类型哪个好不是个好问题。更好的问题是:我们应该静态执行什么?

合理决策:以事实为依据的理性讨论。

作者: ntestoc

Created: 2019-01-09 三 11:07

programming-languages学习笔记--第7部分的更多相关文章

  1. CUDA Programming Guide 学习笔记

    CUDA学习笔记 GPU架构 GPU围绕流式多处理器(SM)的可扩展阵列搭建,每个GPU有多个SM,每个SM支持数百个线程并发执行.目前Nvidia推出了6种GPU架构(按时间顺序,详见下图):Fer ...

  2. Programming Erlang 学习笔记(一)

    入门 启动Shell 在cmd中输入命令”erl”,百分号(%)表示一个注释的开始,从百分号开始到这行结束的所有文本都被看做是注释. 一个完整的命令需要以一个句点和一个回车结束. 退出erlang的命 ...

  3. UIView Programming Guide学习笔记

    |View |Creating and Configuring View Objects |Creating and Managing a View Hierarchy |Adjusting the ...

  4. The C++ Programming Language 学习笔记 第7章 函数

    1.关于内联函数(inline)      借用一下书中的例子. inline int fac(int n) { ) ? :n*fac(n-); }      inline描述符给编译器一个提示,要求 ...

  5. The C++ Programming Language 学习笔记 第6章 表达式和语句

    1.关于strcpy函数. 书中说c风格的字符串尽量少用,strcpy这样的函数应该也要少用.这里讲这个函数主要是要通过本章课后练习第十题来讲一下前面提及的要点.巩固一下前几章的知识.写了一段,本来感 ...

  6. The C++ Programming Language 学习笔记 第5章 指针、数组和结构

    1.关于输出指向字符的指针的值. 现在定义,char c='a',char* pc=&c.在C中,输出该值只需要printf("%p\n",pc);而在C++中,如果cou ...

  7. The C++ Programming Language 学习笔记 第四章 类型和声明

    1.关于main 函数中的 return 0 C99标准中,main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的激活者(如操作系统).如果 main 函数的最后没有写 return ...

  8. 3D Game Programming withDX11 学习笔记(一) 数学知识总结

    在图形学中,数学是不可或缺的一部分,所以本书最开始的部分就是数学知识的复习.在图形学中,最常用的是矢量和矩阵,所以我根据前面三个章节的数学知识,总结一下数学知识. 一.矢量 数学中的矢量,拥有方向和长 ...

  9. Learning ROS for Robotics Programming Second Edition学习笔记(十) indigo Gazebo rviz slam navigation

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 moveit是书的最后一章,由于对机械臂完全不知,看不懂 ...

  10. Learning ROS forRobotics Programming Second Edition学习笔记(八)indigo rviz gazebo

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...

随机推荐

  1. Visual Studio for Mac 安装无响应或者无法连接网络等解决方法

    1.无法连接到网络 2.点击安装和更新无响 这两种情况造成的原因都是由于被墙的原因,第一种情况有部分可以通过fq解决,第二种情况是我遇到过的 反正我全局也失败 这里给出一个我自己用过的解决方案 查看控 ...

  2. [linux] C语言Linux系统编程-做成守护进程

    守护进程: 必须是init进程的子进程,运行在后台,不与任何控制终端相关联. 通过以下步骤成为守护进程 1.调用fork()创建出来一个新的进程,这个新进程会是将来的守护进程 2.在新守护进程的父进程 ...

  3. php之连接mssql(sql server)新手教程

    ps:网上搜了很多教程,讲的都很好,就是都有点漏的地方,花了一天时间查缺补漏终于弄好了(;´༎ຶД༎ຶ`),希望我的教程能帮到新手,还有写博客的时候因为不小心按错一个键,导致重写了,博客园这个编辑器真 ...

  4. IAAS,SAAS,PAAS, CaaS的区别

    来源:云计算头条微信公众号  作者:   你一定听说过云计算中的三个“高大上”的你一定听说过云计算中的三个“高大上”的概念:IaaS.PaaS和SaaS,这几个术语并不好理解.不过,如果你是个吃货,还 ...

  5. C++学习笔记: 智能指针

    c++ 智能指针学习新的 class Simple { public: Simple() { number = param; std::cout << "Simple: &quo ...

  6. 洛谷P3763 [TJOI2017]DNA(后缀数组 RMQ)

    题意 题目链接 Sol 这题打死我也不会想到后缀数组的,应该会全程想AC自动机之类的吧 但知道这题能用后缀数组做之后应该就不是那么难了 首先把\(S\)和\(S0\)拼到一起跑,求出Height数组 ...

  7. ThreeJS文字作为纹理贴图

    文字作为纹理贴图 From:http://www.linhongxu.com/post/view?id=222 这里可以使用canvas作为纹理贴图,Three为我们提供里CanvasTexture ...

  8. 【vue入门】日志demo,增删改查的练习(无vuex版本)

    安装 1. 确定电脑已装node和npm 出现版本号则说明电脑已经安装好node和npm2. 创建一个基于webpack的项目   3. 在项目里安装依赖 4. 运行 配置路由为了动态渲染各个页面的组 ...

  9. Android GridView显示SD卡的图片

    GridView的XML布局: main.xml: <GridViewxmlns:android="http://schemas.android.com/apk/res/android ...

  10. Android 图文混排 通过webview实现并实现点击图片

    在一个开源项目看到是用的webview 实现的 1. 这是在asset中的一个模板html <html> <head> <title>News Detail< ...