programming-languages学习笔记--第7部分
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部分的更多相关文章
- CUDA Programming Guide 学习笔记
CUDA学习笔记 GPU架构 GPU围绕流式多处理器(SM)的可扩展阵列搭建,每个GPU有多个SM,每个SM支持数百个线程并发执行.目前Nvidia推出了6种GPU架构(按时间顺序,详见下图):Fer ...
- Programming Erlang 学习笔记(一)
入门 启动Shell 在cmd中输入命令”erl”,百分号(%)表示一个注释的开始,从百分号开始到这行结束的所有文本都被看做是注释. 一个完整的命令需要以一个句点和一个回车结束. 退出erlang的命 ...
- UIView Programming Guide学习笔记
|View |Creating and Configuring View Objects |Creating and Managing a View Hierarchy |Adjusting the ...
- The C++ Programming Language 学习笔记 第7章 函数
1.关于内联函数(inline) 借用一下书中的例子. inline int fac(int n) { ) ? :n*fac(n-); } inline描述符给编译器一个提示,要求 ...
- The C++ Programming Language 学习笔记 第6章 表达式和语句
1.关于strcpy函数. 书中说c风格的字符串尽量少用,strcpy这样的函数应该也要少用.这里讲这个函数主要是要通过本章课后练习第十题来讲一下前面提及的要点.巩固一下前几章的知识.写了一段,本来感 ...
- The C++ Programming Language 学习笔记 第5章 指针、数组和结构
1.关于输出指向字符的指针的值. 现在定义,char c='a',char* pc=&c.在C中,输出该值只需要printf("%p\n",pc);而在C++中,如果cou ...
- The C++ Programming Language 学习笔记 第四章 类型和声明
1.关于main 函数中的 return 0 C99标准中,main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的激活者(如操作系统).如果 main 函数的最后没有写 return ...
- 3D Game Programming withDX11 学习笔记(一) 数学知识总结
在图形学中,数学是不可或缺的一部分,所以本书最开始的部分就是数学知识的复习.在图形学中,最常用的是矢量和矩阵,所以我根据前面三个章节的数学知识,总结一下数学知识. 一.矢量 数学中的矢量,拥有方向和长 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(十) indigo Gazebo rviz slam navigation
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 moveit是书的最后一章,由于对机械臂完全不知,看不懂 ...
- Learning ROS forRobotics Programming Second Edition学习笔记(八)indigo rviz gazebo
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...
随机推荐
- C# 时间操作类
using System; namespace DotNet.Utilities { /// <summary> /// 时间类 /// 1.SecondToMinute(int Seco ...
- JS实现二叉树的创建和遍历
1.先说二叉树的遍历,遍历方式: 前序遍历:先遍历根结点,然后左子树,再右子树 中序遍历:先遍历左子树,然后根结点,再右子树 后续遍历:先遍历左子树,然后右子树,再根结点 上代码:主要还是利用递归 ...
- Thymeleaf学习记录(2)--自动编译设置
了方便每次修改HTML文件都能实时刷新,做一下更改. 在application.properties文件加入以下命令: #thymeleaf start spring.thymeleaf.mode=H ...
- LinkedList实现队列存储结构
package com.tercher.demo; import java.util.LinkedList; public class Queue { //用LinkedList 实现队列的数据存储结 ...
- 如何面试Web前端开发
分享一篇HR前端面试心得: 面试前端工程师对我来说是一件非常有意思的事,因为面试过程很大程度上也是自我提升的过程.无论大公司还是小公司,之所以在如何招聘到真正有能力的,前端工程师方面会遇到同样的问题. ...
- 教程:让你的表单升级到CSS3和HTML5客户端验证
今天我们一起来看看如何创建一个实用并且功能强大的表单,表单使用如今最热门的技术HTML5和css3来创建,并且可以通过HTML5进行客户端验证. 查看预览下载附件 第一步:策划表单功能 首先,我们得为 ...
- JSP总结摘要
一 概述 1.什么是JSP? Java Server Pages,服务器端负责页面展示的技术,可以嵌套java语言,以取代使用Servlet生成页面的方式,底层被转化为Servlet. 二 路径 1. ...
- jQuery和MVVM类框架的编程区别点
本文说的mvvm框架以react为列,其他应该也是类似的: react实际上仅仅是帮助我们再View层简化,让我们仅仅需要专注数据,只要数据改变,所有的视图就会自己跟随着改变, 本人自己做react项 ...
- js解决千分符问题[收藏下]
//js数字千分符处理 function commafy(num) { num = num + ""; var re = /(-?\d+)(\d{3})/ while (re.te ...
- (WCF) 多线程 (Multi-threading) 和 并发性 (Concurency)
问题:WCF 有个Server端,还有个Client端,他们之间是如何进行并发,多线程通信的呢?多个Client端同时访问Server,如何保证Server端的操作线程安全呢? 在理解WCF Conc ...