Writeup about the subtyping problems.
This commit is contained in:
parent
8862166793
commit
6d88845985
93
graph-lib/graph/graph-6-rich-returns-problem.scrbl
Normal file
93
graph-lib/graph/graph-6-rich-returns-problem.scrbl
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#lang scribble/lp2
|
||||||
|
|
||||||
|
@section{Problem 1}
|
||||||
|
|
||||||
|
For the rich-returns graph, we want to detect nodes from the
|
||||||
|
first step, and convert them to the appropriate value for
|
||||||
|
the second step.
|
||||||
|
|
||||||
|
The issue is that @racket[tmpl-replace-in-instance]
|
||||||
|
requires us to provide a predicate which matches the types
|
||||||
|
we should replace.
|
||||||
|
|
||||||
|
Variants should all be subtypes of the global Tagged type,
|
||||||
|
so that we can access their contents via the @racket[dot]
|
||||||
|
operator without having to know the variant's tag (when
|
||||||
|
accessing a node's content: nodes are tagged).
|
||||||
|
|
||||||
|
@section{Problem 2}
|
||||||
|
|
||||||
|
Graph subtyping: It seems reasonable to not allow arbitrary
|
||||||
|
subtyping for graphs, as a pass might not be semantically
|
||||||
|
correct if it ignores some of the nodes of the graph. For
|
||||||
|
example, let's say we have a replace-arithmetic pass:
|
||||||
|
|
||||||
|
@chunk[<replace-arithmetic-pass>
|
||||||
|
(define-pass replace-arithmetic
|
||||||
|
[(add a b) (arithmetic 'add a b)]
|
||||||
|
[(mul a b) (arithmetic 'mul a b)]
|
||||||
|
[(div a b) (arithmetic 'div a b)])]
|
||||||
|
|
||||||
|
If the pass above is called on a graph @racket[g-div] where
|
||||||
|
divisions are already marked as safe (@racket[b] is never
|
||||||
|
@racket[0]) or unsafe (@racket[b] can be @racket[0]), the
|
||||||
|
resulting graph will have @racket[arithmetic] nodes
|
||||||
|
@emph{and} @racket[div-unsafe] ones, whereas one would
|
||||||
|
semantically expect the @racket[div-unsafe] nodes to have
|
||||||
|
been merged too:
|
||||||
|
|
||||||
|
@chunk[<g-div>
|
||||||
|
(define-graph (g-div)
|
||||||
|
(var [name : String])
|
||||||
|
(add [a : var] [b : var])
|
||||||
|
(mul [a : var] [b : var])
|
||||||
|
(div [a : var] [b : var])
|
||||||
|
(div-unsafe [a : var] [b : var] [on-error-message : String])
|
||||||
|
other-nodes …)]
|
||||||
|
|
||||||
|
We do however wish to be able to test a pass without having
|
||||||
|
to care about the irrelevant nodes. We could specify a union
|
||||||
|
of graph types when writing the pass. The
|
||||||
|
@racket[replace-arithmetic] pass would then be declared as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
@chunk[<replace-arithmetic-pass>
|
||||||
|
(define-pass (replace-arithmetic [x : (U g g-test)]) : g-arith
|
||||||
|
[(add a b) (arithmetic 'add a b)]
|
||||||
|
[(mul a b) (arithmetic 'mul a b)]
|
||||||
|
[(div a b) (arithmetic 'div a b)])]
|
||||||
|
|
||||||
|
Where @racket[g] has been declared using:
|
||||||
|
|
||||||
|
@chunk[<g>
|
||||||
|
(define-graph (g)
|
||||||
|
(var [name : String])
|
||||||
|
(add [a : var] [b : var])
|
||||||
|
(mul [a : var] [b : var])
|
||||||
|
(div [a : var] [b : var])
|
||||||
|
other-nodes …)]
|
||||||
|
|
||||||
|
And @racket[g-test], which does not contain the
|
||||||
|
@racket[other-nodes] has been declared using:
|
||||||
|
|
||||||
|
@chunk[<g-test>
|
||||||
|
(define-graph (g-test)
|
||||||
|
(var [name : String])
|
||||||
|
(add [a : var] [b : var])
|
||||||
|
(mul [a : var] [b : var])
|
||||||
|
(div [a : var] [b : var]))]
|
||||||
|
|
||||||
|
The @racket[g-test] declaration could easily be derived
|
||||||
|
from the pass declaration, by removing the node types not
|
||||||
|
mentionned within.
|
||||||
|
|
||||||
|
@subsection{Graph operations}
|
||||||
|
|
||||||
|
The graph operations should not require specifying the type
|
||||||
|
however: ideally, one should write the input graph type name
|
||||||
|
only in the parameter list of the pass, and not have to
|
||||||
|
refer to it within the pass body for common operations. Thes
|
||||||
|
operations include:
|
||||||
|
|
||||||
|
@chunk[<*>
|
||||||
|
(begin)]
|
151
graph-lib/graph/problems.rkt
Normal file
151
graph-lib/graph/problems.rkt
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
#lang typed/racket
|
||||||
|
|
||||||
|
(require typed/rackunit)
|
||||||
|
|
||||||
|
;; tagged
|
||||||
|
(begin
|
||||||
|
;; Base struct for all tagged values
|
||||||
|
(struct (A B) tagged ([tag : A] [value : B]))
|
||||||
|
|
||||||
|
;; (define-tagged a T)
|
||||||
|
;; Use tag-a just for match and (get …)
|
||||||
|
(struct tag-a ()) ;; "remember" this one
|
||||||
|
(define-type (tagged-a T) (tagged tag-a T))
|
||||||
|
;; (define-tagged #:uninterned a T)
|
||||||
|
(struct tag-a-1 tag-a ()) ;; do not "rembmer" the #:uninterned ones
|
||||||
|
(define-type (tagged-a-1 T) (tagged tag-a-1 T))
|
||||||
|
;; (define-tagged #:uninterned a T)
|
||||||
|
(struct tag-a-2 tag-a ())
|
||||||
|
(define-type (tagged-a-2 T) (tagged tag-a-2 T))
|
||||||
|
;; (define-tagged #:uninterned a T)
|
||||||
|
(struct tag-a-3 tag-a ())
|
||||||
|
(define-type (tagged-a-3 T) (tagged tag-a-3 T))
|
||||||
|
;; (define-tagged b T)
|
||||||
|
(struct tag-b ())
|
||||||
|
(define-type (tagged-b T) (tagged tag-b T))
|
||||||
|
|
||||||
|
;; instanciation:
|
||||||
|
(tagged (tag-a) "ice")
|
||||||
|
|
||||||
|
(define-syntax-rule (define-pred name? tag)
|
||||||
|
(define-match-expander name?
|
||||||
|
(λ (stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ v)
|
||||||
|
#'(and (? name?) (app tagged-value v))]))
|
||||||
|
(λ (stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ v)
|
||||||
|
#'(let ([v-cache v])
|
||||||
|
;; make-predicate works for polymorphic structs when
|
||||||
|
;; instantiating them with Any.
|
||||||
|
(and ((make-predicate (tagged Any Any)) v-cache)
|
||||||
|
((make-predicate tag) (tagged-tag v-cache))))]))))
|
||||||
|
|
||||||
|
(define-pred tagged-a-1? tag-a-1)
|
||||||
|
(define-pred tagged-a-2? tag-a-2)
|
||||||
|
(define-pred tagged-a? tag-a)
|
||||||
|
(define-pred tagged-b? tag-b)
|
||||||
|
|
||||||
|
(check-true (tagged-a? (tagged (tag-a) "water")))
|
||||||
|
(check-true (tagged-a-1? (tagged (tag-a-1) "fire")))
|
||||||
|
(check-true (tagged-a-1? (tagged (tag-a-1) "air")))
|
||||||
|
(check-false (tagged-a-1? (tagged (tag-a-2) "earth")))
|
||||||
|
(check-false (tagged-a-1? (tagged (tag-a) "salt")))
|
||||||
|
(check-false (tagged-b? (tagged (tag-a) "mercury")))
|
||||||
|
(check-false (tagged-b? (tagged (tag-a-1) "alchemy")))
|
||||||
|
|
||||||
|
(λ ([x : (U (tagged-a-1 String)
|
||||||
|
(tagged-a-2 Number)
|
||||||
|
(tagged-b Symbol))])
|
||||||
|
: (U Number String)
|
||||||
|
;; Match should expand to this:
|
||||||
|
(match x
|
||||||
|
[(tagged-a? v) v]
|
||||||
|
[(tagged-b? v) (symbol->string v)]))
|
||||||
|
|
||||||
|
(λ ([x : (U (tagged-a-1 String)
|
||||||
|
(tagged-a-2 String)
|
||||||
|
(tagged tag-a String)
|
||||||
|
(tagged-b Symbol))])
|
||||||
|
: String
|
||||||
|
;; Match should expand to this
|
||||||
|
(match x
|
||||||
|
[(tagged-a-1? v) (string-append v "-1")]
|
||||||
|
[(tagged-a-2? v) (string-append v "-2")]
|
||||||
|
[(tagged-a? v) (string-append v "-base")]
|
||||||
|
[(tagged-b? v) (symbol->string v)])))
|
||||||
|
|
||||||
|
;; struct
|
||||||
|
(begin
|
||||||
|
;; The structs seem to work well the way they are currently defined (using
|
||||||
|
;; "remember" to know all the structs with a given field).
|
||||||
|
|
||||||
|
;; (define-struct [x : Number] [y : String])
|
||||||
|
(struct (X Y) structure+x+y ([x : X] [y : Y])) ;; "remember"
|
||||||
|
(define-type structure+x=number+y=string (structure+x+y Number String))
|
||||||
|
|
||||||
|
;; (define-struct [x : Number] [y : String] [z : Symbol])
|
||||||
|
(struct (X Y Z) structure+x+y+z ([x : X] [y : Y] [z : Z])) ;; "remember"
|
||||||
|
(define-type structure+x=number+y=string+z=symbol
|
||||||
|
(structure+x+y+z Number String Symbol))
|
||||||
|
|
||||||
|
;; (define-struct [z : Symbol])
|
||||||
|
(struct (Z) structure+z ([z : Z])) ;; "remember"
|
||||||
|
(define-type structure+z=symbol (structure+z Symbol))
|
||||||
|
|
||||||
|
;; (define-struct [x : Symbol])
|
||||||
|
(struct (X) structure+x ([x : X])) ;; "remember"
|
||||||
|
(define-type structure+x=symbol (structure+x Symbol))
|
||||||
|
|
||||||
|
;; (has-get [x Number])
|
||||||
|
(define-type (has-get+x X) (U (structure+x X)
|
||||||
|
(structure+x+y X Any)
|
||||||
|
(structure+x+y+z X Any Any)))
|
||||||
|
(define-type has-get+x=number (has-get+x Number))
|
||||||
|
|
||||||
|
;; (has-get [y Number])
|
||||||
|
(define-type (has-get+y Y) (U (structure+x+y Any Y)
|
||||||
|
(structure+x+y+z Any Y Any)))
|
||||||
|
(define-type has-get+y=number (has-get+y Number))
|
||||||
|
|
||||||
|
;; (has-get [z Number])
|
||||||
|
(define-type (has-get+z Z) (U (structure+z Z)
|
||||||
|
(structure+x+y+z Any Any Z)))
|
||||||
|
(define-type has-get+z=number (has-get+z Number))
|
||||||
|
|
||||||
|
;; (get v x)
|
||||||
|
(define get-x
|
||||||
|
(λ #:∀ (X) ([s : (has-get+x X)]) : X
|
||||||
|
(cond
|
||||||
|
[(structure+x? s) (structure+x-x s)]
|
||||||
|
[(structure+x+y? s) (structure+x+y-x s)]
|
||||||
|
[(structure+x+y+z? s) (structure+x+y+z-x s)])))
|
||||||
|
|
||||||
|
;; (get v y)
|
||||||
|
(define get-y
|
||||||
|
(λ #:∀ (Y) ([s : (has-get+y Y)]) : Y
|
||||||
|
(cond
|
||||||
|
[(structure+x+y? s) (structure+x+y-y s)]
|
||||||
|
[(structure+x+y+z? s) (structure+x+y+z-y s)])))
|
||||||
|
|
||||||
|
;; (get v z)
|
||||||
|
(define get-z
|
||||||
|
(λ #:∀ (Z) ([s : (has-get+z Z)]) : Z
|
||||||
|
(cond
|
||||||
|
[(structure+z? s) (structure+z-z s)]
|
||||||
|
[(structure+x+y+z? s) (structure+x+y+z-z s)]))))
|
||||||
|
|
||||||
|
;; graph
|
||||||
|
(begin
|
||||||
|
;; define-graph
|
||||||
|
(define-type g-test (tagged-a-1 (structure+x+y String g-test)))
|
||||||
|
(define-type g (tagged-a-2 (structure+x+y+z String g Number)))
|
||||||
|
;; pass:
|
||||||
|
(λ ([root-node : (U g g-test)])
|
||||||
|
(ann (match root-node
|
||||||
|
[(tagged-a-1? s) (string-length (get-x s))]
|
||||||
|
[(tagged-a-2? s) (get-z s)])
|
||||||
|
Number)
|
||||||
|
(cons (get-x (tagged-value root-node))
|
||||||
|
(get-y (tagged-value root-node)))))
|
Loading…
Reference in New Issue
Block a user