编程语言设计

本文探讨编程语言设计,涵盖语法综述,包括语法关键字与变量的绑定;标识符的语义,分为变量声明、语句和表达式;类型设计,强调类型对函数接口的限制;以及多态的实现,通过类型变量的泛化和实例化。文章还提及并行和分布式计算在语言设计中的应用。
摘要由CSDN通过智能技术生成

语法综述

语言中的标识符分为两种类型, 一类是用于命名语法、符号等抽象概念,另外一类是用于命名数据的存储位置, 前者被称之为"语法关键字",后面一类被称之为变量,常量。

标识符的两种作用, 我们使用"绑定"这一个动作来实现, 这个绑定是有结果的。

  • 语法关键字是对语义逻辑的绑定结果,是对作用域的限定。
    注:也就是在什么地方位置干什么样的事情
  • 变量与它所存储数据和位置性质的限定。

标识符所绑定的语义

  • 纯粹陈述数据的绑定叫做, 变量声明
  • 纯粹陈述逻辑的绑定叫做,语句
  • 陈述数据与逻辑的绑定叫做,表达式

类型的设计

类型的本质就是对函数接口的限制,我们使用->表示对类型的限制

a -> a

如何获得一个函数的类型呢?

  1. 首先给函数参数一个类型变量
  2. 递归的处理函数中的变量的类型

多态:

(define f 
	(lambda(x) x)
(f 1)
(f True)

首先我们可以知道f的类型(a -> a), 当使用(f 1)进行调用的时候它的类型就变成了 (int -> int),这是为什么? 因为当我们调用(f 1)的时候a的类型就被限制为int, 这个类型变量一旦被限制, 那么永远就被限制了。然后当我们调用(f true)的时候就会出现类型错误。

所以我们在lamnda处进行泛化(将RHS上的每个不受约束的变量视为“普遍量化”)。然后在每次函数调用的时候创建一个f的类型的实例

这样f的类型就变成了:

∀a || a -> a
  1. 当我们使用(f 1)的时候得到(a1 -> a1)
  2. 当我们使用(f true)的时候得到(a2 -> a2)

此时f的类型是什么∀a.a -> a, 它是没有任何变化的, 这是因为类型变量是在它们自已的实例中被限制的。但是函数的类型依然是原来的。

我们接下来看下面的例子,

(define a [1 2 3 4])
  1. 当使用[X]的时候我们期待, 它希望可以将X取出来并将其作为原始类型A使用
  2. 不管你往里面放入什么样子的东西, 它一定是与类型A进行兼容的。A的子类型是a
  3. 然后我们使用[A]用作类型检查

但是在H-M的类型系统中, 就会出现一种非常诡异的现象

let a = [\x -> x] 类型为: [a.a -> a], 但是得到∀a.[a -> a]
let b = [a]       类型为[[a.a -> a]], 但是得到∀a[[a -> a]]

你有没有发现这个一阶的逻辑的符号不取决于右边的内容, 是取决于右边内容所在的位置。

这就出现了一些问题, 所以我们继续来看:

let r = ref(\x -> x) in
	(r := \x -> x + 1; (!r)true)

首先我们根据H-M类型系统来模拟上面的行为:

r的类型是∀a.ref(a -> a)

上面说过这个∀a的意思是在使用这个东西的时候创建一个它的实例,所以你会得到

ref(a1 -> a1)
ref(a2 -> a2)

看到了有没有这很神奇。

下面是具体的使用方法:

  1. 类型限制的实质是指,记录参数是如何被使用的在函数体中。
  2. 如果类型变量不应该进一步被限制, 那么它应该被泛化
  3. 当一个类型变量被泛化的时候, 无法在使用该类型变量的上下文中再对其进行额外的限制
public class Example<T> {
    private T value;

    public Example(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public static void main(String[] args) {
        Example<?> example = new Example<>("Hello");
        Object value = example.getValue();
        // 这里我们只能使用Object来引用类型变量的值
    }
}
  1. 函数的调用不可以限制参数的类型, 只有函数的定义可以。
  2. 所以参数的类型限制只可以在函数的边界上

并行和分布式计算

可以实现简单的并行计算, 这与C#的async/wait优点类似, 但是它的设计目标是让这种原语成为系统中唯一的并行操作原语, 从而使得并行计算可以极其容易实现。我们提供一个关键字叫做par, 用于指定代码可以并行执行, 比如你可以这样做:

(:x (par (long-computation)))
(:y x)
(do-other-things)
(foo (wait y))

这里的par表示:(long-computation)应该被自动安排为异步执行。

第二行的y能够直接得到x的可能还没有计算的值(在C#里面叫做Task), 由于还没有人使用y的值, 我们并不需要检查这个long-com是否已经执行完毕, 所以(:y x)和(do-other-things)可以毫无停顿的进行。这样(long-com)就和(:y x)…(doo)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值