如果在键入/球拍中我定义了三个struct
s:
(struct: foo ([a : Number]))
(struct: bar ([b : String]))
(struct: st ([a : Number] [b : String] [c : Number]))
如何让st
同时成为foo
和bar
的子类型,以便以下两项工作?
((λ: ([x : bar]) x) (st 1 "b" 3))
((λ: ([x : foo]) x) (st 1 "b" 3))
我对任何可以提供类似功能的解决方案或黑客感兴趣,无论是多重继承,还是通过函数或其他方式重新实现struct
。我已经通过宏定义了struct
s,所以如果我必须生成一些样板文件,那就不重要了。
答案 0 :(得分:1)
数学上,(纯函数,不可变)struct
是一个将字段名称映射到值的函数。因为在typed/racket
中,映射一小组输入的函数类型是映射更大输入集的函数类型的子集,我们可以通过函数模拟结构,并使用宏来表示一些语法糖。
请注意,如果您使用的是更新版本的typed / racket,则可能需要稍微调整类型语法,即将箭头→
移动到其括号的开头,或许还有其他一些小调整。
; If it quacks…
(require (for-syntax racket/syntax))
(require (for-syntax syntax/parse))
(define-syntax (duck stx)
(syntax-parse stx
[(_ name:id ((field:id (~datum :) type) ...))
(define/with-syntax make-name (format-id #'name "make-~a" #'name))
(define/with-syntax (name-field ...) (map (λ (f) (format-id #'name "~a-~a" #'name f)) (syntax-e #'(field ...))))
#'(begin
(define-type name
(case→
['field → type] ...)
#:omit-define-syntaxes)
(: make-name (type ... → name))
(define (make-name field ...)
(λ (field-selector)
(cond
[(eq? field-selector 'field) field] ...)))
; Remove this line and use (make-mystruct 1 "b" 3)
; instead of the shorthand (mystruct 1 "b" 3)
; if #:omit-define-syntaxes stops working.
(define name make-name)
(begin
(: name-field (name -> type))
(define (name-field x)
(x 'field)))
...
)]))
用法:
(duck dfoo ([a : Number]))
(duck dbar ([b : String]))
(duck dbaz ([c : String]))
(duck dquux ([a : Number] [d : Number]))
(duck dfloz ([a : Number] [c : Number]))
(duck dst ([a : Number] [b : String] [c : Number]))
(define upcast-foo ((λ: ([x : dfoo]) x) (dst 1 "b" 3)))
(define upcast-bar ((λ: ([x : dbar]) x) (dst 1 "b" 3)))
; This one fails because dbaz has c : String instead of c : Number
; (define result-baz ((λ: ([x : dbaz]) x) (dst 1 "b" 3)))
; This one is not even close (wrong field name)
; (define result-quux ((λ: ([x : dquux]) x) (dst 1 "b" 3)))
(define upcast-floz ((λ: ([x : dfloz]) x) (dst 1 "b" 3)))
(dfoo-a upcast-foo) ; 1
(dbar-b upcast-bar) ; "b"
(dfloz-a upcast-floz) ; 1
(dfloz-c upcast-floz) ; 3
; Fails with error: "Type Checker: Expected dfoo, but got dbar in: upcast-bar"
; (dfoo-a upcast-bar)
duck
宏为dst
生成此代码:
(define-type dst
(case->
['a -> Number]
['b -> String]
['c -> Number])
#:omit-define-syntaxes)
(: make-dst (Number String Number -> dst))
(define (make-dst a b c)
(λ (field-name)
(cond
[(eq? field-name 'a) a]
[(eq? field-name 'b) b]
[(eq? field-name 'c) c])))
(define dst make-dst)
(begin
(: dst-a (dst -> Number))
(define (dst-a x)
(x 'a)))
(begin
(: dst-b (dst -> String))
(define (dst-b x)
(x 'b)))
(begin
(: dst-c (dst -> Number))
(define (dst-c x)
(x 'c)))
答案 1 :(得分:0)
此功能is already implemented适用于typed/racket
中的类(在v6.2.0.2中,也可能在v6.1.1中):
#lang typed/racket
(require (for-syntax syntax/parse))
(require (for-syntax racket/syntax))
(define-syntax (duck stx)
(syntax-parse stx
[(_ (field type) ...)
(define/with-syntax (the-field ...) (map (λ (f) (format-id f "the-~a" f)) (syntax-e #'(field ...))))
(define/with-syntax (get-field ...) (map (λ (f) (format-id f "get-~a" f)) (syntax-e #'(field ...))))
#'(class object%
(super-new)
(init [field : type] ...)
(define the-field : type
field) ...
(define/public (get-field) : type
the-field) ...
)]))
用法:
(: foo (Object (get-x (→ Real)) (get-y (→ String))))
(define foo (new (duck (x Real)
(z Number)
(y String))
[x 42]
[z 123]
[y "y"]))
(send foo get-x)
(send foo get-y)
; (send foo get-z) ;; Does not typecheck, as expected.
但是,它的缺点是无法声明不可变类,因此类型出现不会对类字段起作用,即以下内容不起作用:
(if (zero? (send foo get-x))
(ann (send foo get-x) Zero))