Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

07 Mar 2021

The Racket Guide阅读笔记【七】

The Racket Guide 阅读笔记,chapter 12, 14.

12 Pattern Matching

语法形式:

(match target-expr
  [pattern expr ...+] ...)

pattern可以是

  • 普通常量
  • cons, list, vector的计算结果
  • struct的构造函数
  • 非引用,非构造函数的标识符在pattern里面会被当作变量用来绑定传入的参数(下划线_除外,它用来忽略绑定)

else在match里面不是关键字,可能会对cond以及case表达式造成困扰。

三点...表示Kleen操作,在list或者vector模式里面会重复在前之前的元素。

> (match '(1 2 3 4)
    [(list 1 x ... 4) x])
'(2 3)

> (match (list (hat 23 'bowler) (hat 22 'pork-pie))
    [(list (hat sz styl) ...) (apply + sz)])
45

缩略号也可以嵌套。

准摘引也可以用来构建模式。更多的匹配形式可以参考racket/match。例如match-let以及match-lambda。

13 Classes and Objects

14 Units (Components)

单元用于把程序组织成可编译和重用的单元。单元可以被调用。

14.1 Signatures and Units

单元接口通常按照签名来描述:

#lang racket
 
(define-signature toy-factory^
  (build-toys  ; (integer? -> (listof toy?))
   repaint     ; (toy? symbol? -> toy?)
   toy?        ; (any/c -> boolean?)
   toy-color)) ; (toy? -> symbol?)
 
(provide toy-factory^)

接口的实现使用define-unit来进行:

#lang racket
 
(require "toy-factory-sig.rkt")
 
(define-unit simple-factory@
  (import)
  (export toy-factory^)
 
  (printf "Factory started.\n")
 
  (define-struct toy (color) #:transparent)
 
  (define (build-toys n)
    (for/list ([i (in-range n)])
      (make-toy 'blue)))
 
  (define (repaint t col)
    (make-toy col)))
 
(provide simple-factory@)

14.2 Invoking Units

可以调用一个单元。但是要确保这个单元所需要的名字已经包含了。

因为simple-factory@没有导入其他单元,所以可以直接调用

(invoke-unit simple-factory@)

上面的调用不会导入名字,如果需要导入名字到当前范围的话,需要:

(define-values/invoke-unit/infer simple-factory@)

然后就可以执行:

(define-values/invoke-unit/infer toy-store@)

需要确保相应的模块已经require了。

14.3 Linking Units

可以使用define-compound-unit/infer来链接多个单元:

> (require "toy-factory-sig.rkt")
> (require "toy-store-sig.rkt")
> (require "store-specific-factory-unit.rkt")
> (require "toy-store-unit.rkt")
>  (define-compound-unit/infer toy-store+factory@
    (import)
    (export toy-factory^ toy-store^)
    (link store-specific-factory@
          toy-store@))

14.4 First-Class Units

define-unit的形式也可以改写成:

(define toy-store@
  (unit
   (import toy-factory^)
   (export toy-store^)
 
   (define inventory null)
 
   (define (store-color) 'green)
   ....))

跟define-unit相比,这样做的一个问题是无法推断import和export。 因为define-unit会附加额外的信息。

但是好处是unit的定义可以出现在注入lambda的结构之中,例如:

(define toy-store@-maker
  (lambda (the-color)
    (unit
    ...

这种情况下,要使用define-values/invoke-unit,而不是 define-values/invoke-unit/infer来调用。同样的,导出的时候需要使用compound-unit

14.5 Whole-module Signatures and Units

racket/signature和racket/unit这两个模块名可以用来避免重复性的代码。

例如:

#lang racket/signature
 
build-toys  ; (integer? -> (listof toy?))
repaint     ; (toy? symbol? -> toy?)
toy?        ; (any/c -> boolean?)
toy-color   ; (toy? -> symbol?)

单元的前面toy-factory^是从文件名"toy-factory-sig.rkt"推断出来的。

14.6 Contracts for Units

14.6.1 Adding Contracts to Signatures

#lang racket
 
(define-signature contracted-toy-factory^
  ((contracted
    [build-toys (-> integer? (listof toy?))]
    [repaint    (-> toy? symbol? toy?)]
    [toy?       (-> any/c boolean?)]
    [toy-color  (-> toy? symbol?)])))
 
(provide contracted-toy-factory^)

14.6.2 Adding Contracts to Units

(define-unit/contract wrapped-simple-factory@
  (import)
  (export (toy-factory^
           [build-toys (-> integer? (listof toy?))]
           [repaint    (-> toy? symbol? toy?)]
           [toy?       (-> any/c boolean?)]
           [toy-color  (-> toy? symbol?)]))
...

14.7 unit versus module

从模块化的角度,unit使module更完备

  • module更多用来管理全局命名空间。
  • unit则用来给位接口提供某种实现。

(本篇完)