diff --git a/graph-lib/graph/__DEBUG_graph5.rkt b/graph-lib/graph/__DEBUG_graph5.rkt index 974d8af7..9b26d2c7 100644 --- a/graph-lib/graph/__DEBUG_graph5.rkt +++ b/graph-lib/graph/__DEBUG_graph5.rkt @@ -49,27 +49,28 @@ (for-syntax syntax/parse)) (define-graph/multi-ctor gm ([a [b1 : b] [b2 : b] [s : String] [v : Number]] - [b [a1 : a] [s : String] [v : Number]]) - [(r [v : Integer] [w : String]) - : a - (printf "r ~a ~a\n" v w) - (a (bx (if (> v 0) (sub1 v) (string-length w))) - (by (if (> v 0) (sub1 v) (string-length w)) "xyz") - w - v)] - [(bx [v : Integer]) - : b - (printf "bx ~a\n" v) - (b (r v "one") "x" v)] - [(by [v : Integer] [w : String]) - : b - (printf "by ~a ~a\n" v w) - (b (r v "two") "y" (+ v (string-length w)))]) + [b [a1 : a] [s : String] [v : Number]]) + [(r [v : Integer] [w : String]) + : a + (printf "r ~a ~a\n" v w) + (a (bx (if (> v 0) (sub1 v) (string-length w))) + (by (if (> v 0) (sub1 v) (string-length w)) "xyz") + w + v)] + [(bx [v : Integer]) + : b + (printf "bx ~a\n" v) + (b (r v "one") "x" v)] + [(by [v : Integer] [w : String]) + : b + (printf "by ~a ~a\n" v w) + (b (r v "two") "y" (+ v (string-length w)))]) (define gmi (gm 3 "b")) (check-equal?: (get gmi v) 3) (check-equal?: (get gmi b1 v) 2) (check-equal?: (get gmi b1 s) "x") (check-equal?: (get gmi b1 a1 v) 2) + ;(check-equal?: (get gmi b1 a1 b1 a1 v) 1) ;(check-equal?: (get gmi b1 a1 b1 a1 b1 v) 1) diff --git a/graph-lib/graph/graph-6-rich-returns.lp2.rkt b/graph-lib/graph/graph-6-rich-returns.lp2.rkt index b514cf7c..430bfe6f 100644 --- a/graph-lib/graph/graph-6-rich-returns.lp2.rkt +++ b/graph-lib/graph/graph-6-rich-returns.lp2.rkt @@ -101,6 +101,7 @@ plain list. @chunk[ (define-syntax/parse (define-temp-ids "first-step" name) + (define-temp-ids "first-step-expander2" name) (define-temp-ids "~a/simple-mapping" (node …)) (define-temp-ids "~a/node" (mapping …)) (template @@ -126,15 +127,30 @@ encapsulating the result types of mappings. @chunk[ (define-type-expander (~> stx) (syntax-parse stx - [(_ (~literal mapping)) + [(_ (~datum mapping)) ;; TODO: should be ~literal (template (U (first-step #:placeholder mapping/node) (tmpl-replace-in-type result-type [node (first-step #:placeholder node)] …)))] - …))] + … + ;; TODO: should fall-back to outer definition of ~>, if any. + )) + (define-type-expander (first-step-expander2 stx) + (syntax-parse stx + [(_ (~datum mapping)) ;; TODO: should be ~literal + (template + (U (first-step #:placeholder mapping/node) + (tmpl-replace-in-type result-type + [node (first-step #:placeholder node)] + …)))] + … + ;; TODO: should fall-back to outer definition of ~>, if any. + ) + #;(U (first-step #:placeholder m-streets4/node) + (Listof (first-step #:placeholder Street))))] -@; TODO: replace-in-type doesn't work well here, we need to define a +@; TODO: replace-in-type doesn't work wfell here, we need to define a @; type-expander. @chunk[ (tmpl-replace-in-type field-type @@ -237,27 +253,37 @@ encapsulating the result types of mappings. (Street Street2/simple-mapping)) (map Street snames)))))))|# -(begin +#;(begin (define-graph first-step #:definitions ((define-type-expander (~> stx) (syntax-parse stx - ((_ (~literal m-cities)) + ((_ (~datum m-cities));(~literal m-cities)) (template (U (first-step #:placeholder m-cities3/node) (tmpl-replace-in-type (Listof City) (City (first-step #:placeholder City)) (Street (first-step #:placeholder Street)))))) - ((_ (~literal m-streets)) + ((_ (~datum m-streets));(~literal m-streets)) (template (U (first-step #:placeholder m-streets4/node) (tmpl-replace-in-type (Listof Street) (City (first-step #:placeholder City)) - (Street (first-step #:placeholder Street))))))))) + (Street (first-step #:placeholder Street)))))))) + (define-type-expander + (~~> stx) + (template (U + (first-step #:placeholder m-streets4/node) + (tmpl-replace-in-type + (Listof Street) + (City (first-step #:placeholder City)) + (Street (first-step #:placeholder Street))))) + #;#'(U (first-step #:placeholder m-streets4/node) + (Listof (first-step #:placeholder Street))))) #| (City [foo : Number] ((m1) (City 1))) (Street [foo : Number] ((m2) (Street 2))) @@ -269,9 +295,9 @@ encapsulating the result types of mappings. (City (streets : (U m-streets4/node (Listof Street))) ((City1/simple-mapping - (streets : #|(~> m-streets)|# - (U (first-step #:placeholder m-streets4/node) - (Listof (first-step #:placeholder Street))) + (streets : (~> m-streets);(Let [~> ~~>] (~> m-streets)) + #|(U (first-step #:placeholder m-streets4/node) + (Listof (first-step #:placeholder Street)))|# )) (City streets))) (Street @@ -327,6 +353,140 @@ encapsulating the result types of mappings. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +(begin + (define-graph + first-step + #:definitions + ((define-type-expander + (~> stx) + (syntax-parse + stx + ((_ (~datum m-cities)) + (template + (U + (first-step #:placeholder m-cities3/node) + (Listof (first-step #:placeholder City))))) + ((_ (~datum m-streets)) + (template + (U + (first-step #:placeholder m-streets4/node) + (Listof (first-step #:placeholder Street))))))) + #;(define-type-expander + (first-step-expander2 stx) + (syntax-parse + stx + ((_ (~literal m-cities)) + (template + (U + (first-step #:placeholder m-cities3/node) + (Listof (first-step #:placeholder City))))) + ((_ (~literal m-streets)) + (template + (U + (first-step #:placeholder m-streets4/node) + (Listof (first-step #:placeholder Street))))))) + (define-type-expander + (~~> stx) + #;(template (U + (first-step #:placeholder m-streets4/node) + (tmpl-replace-in-type + (Listof Street) + (City (first-step #:placeholder City)) + (Street (first-step #:placeholder Street))))) + #'(U m-streets4/node (Listof Street)))) + (City + (streets : (Let [~> ~~>] (~> m-streets))#;(~> m-streets)) + ((City1/simple-mapping (streets : (~> m-streets))) (City streets))) + (Street + (sname : String) + ((Street2/simple-mapping (sname : String)) (Street sname))) + (m-cities3/node + (returned : (Listof City)) + ((m-cities (cnames : (Listof (Listof String)))) + (m-cities3/node + (let ((City City1/simple-mapping) (Street Street2/simple-mapping)) + (define (strings→city (s : (Listof String))) (City (m-streets s))) + (map strings→city cnames))))) + (m-streets4/node + (returned : (Listof Street)) + ((m-streets (snames : (Listof String))) + (m-streets4/node + (let ((City City1/simple-mapping) (Street Street2/simple-mapping)) + (map Street snames))))))) + + + + + + + + + + + diff --git a/graph-lib/graph/graph-aliasing.lp2.rkt b/graph-lib/graph/graph-aliasing.lp2.rkt new file mode 100644 index 00000000..88bcc115 --- /dev/null +++ b/graph-lib/graph/graph-aliasing.lp2.rkt @@ -0,0 +1,120 @@ +#lang scribble/lp2 +@(require "../lib/doc.rkt") +@doc-lib-setup + +@title[#:style manual-doc-style]{Type and constructor + aliases in graph declarations} + +@(table-of-contents) + +@section{Introduction} + +When declaring a graph, the names of its nodes and mappings +as well as those of the graph it is based on may collide. We +try here to provide reasonnable defaults indicating which +name should refer to what at each point. + +@chunk[ + (graph g-old + [City [streets : (Listof Street)]] + [Street [name : String]] + mappings…) + (pass g-old → g-new + ([City [streets : (Listof Street)] [nstreets : Index]] + [Street [name : String]]) + (m-city ([g g-old.City]) : City + (City g.streets (length g.streets))))] + +Capitalization aside, the name @tc[city] could refer to seven different things: +@itemlist[ + @item{The @racket[g-old.city] type} + @item{The incomplete @racket[g-new.city] type} + @item{The placeholder @racket[g-new.city] type} + @item{The @racket[g-new.city] type, but most of the time this one should be + needed only outside the graph declaration} + @item{The @racket[city] constructor, returning an incomplete node} + @item{The @racket[city] mapping, returning a placeholder} + @item{A @racket[city] field, but this one is not a problem, since it will + always be accessed via @racket[some-node-instance.city]} + @item{The return type of the @racket[city] mapping, as in @racket[~> city]}] + +Furthermore, the @racket[city] constructor should accept several cases for +@tc[street], when giving the list of values for the @tc[streets] field: +@itemlist[ + @item{A placeholder for @racket[street]} + @item{An incomplete @racket[street]} + @item{A @racket[g-old.street], which it will convert to the new type using the + implicit mapping}] + +In the field types, we have one case: +@itemlist[ + @item{The @racket[street] name, for example, should refer + to the with-promises type of the new graph @racket[g-new], + once the graph is fully constructed. When declaring the incomplete type for + @racket[city], @racket[street] will refer to the incomplete @racket[street], + but this is happening behind the scenes, and shouldn't cause any ambiguity.}] + +In the mapping declaration, we have four cases: +@itemlist[ + @item{Return type: this should obviously refer to the name + of the new node type (incomplete or placeholder).} + @item{Parameter type: it would be tempting to make node names there refer to + old node types. However, passing around placeholders and incomplete nodes then + becomes difficult.} + @item{Body, used as a function: inside the body, a node name should refer to + the constructor for the new node type, returning a placeholder.} + @item{Body, used as a type. A case where this could happen is when declaring an + inner function. The case isn't clear here, as the function could return an + incomplete node, or a placeholder. The same goes for the parameter type, or + the type of variables bound by let.}] + +The type and constructor aspects are independant, as the +same identifier can be used both as a type and as a function +without ambiguity. It would however be more intuitive if the +return type of @tc[city] when used as a function was a +subtype of what @tc[city] expands to when used as a type, so +that @tc[(ann (city x y) city)] is always valid. + +It is important to note that once constructed, incomplete nodes can be made +opaque without loss of functionality. In other words, we are free to use +@tc[(U incomplete placeholder)] as the type for all constructed nodes. + +We could therefore use @tc[(U incomplete placeholder)] everywhere, except for +the parameters, where we need to access the old graph types, which may conflict +with the new ones. + +When there is a single mapping for the node, we have, schematically: + +@chunk[ + (g-old.city → g-new.city + (begin … + (city x y) + (bar other-old-city)))] + +Where @tc[bar] is the constructor for another node taking a city for one of its +fields (here it will auto-call the mapping). + +@section{Conclusion} + +The solution we propose is to use @tc[(U incomplete placeholder)] everywhere for +the types, and disambiguate the old types in the parameters with @tc[old.city]. +When used as a function, the name should refer to the constructor. We do not +have to worry about calling auto-defined mappings, as the constructors should +accept the old nodes, and convert them behind the scenes. + +The only problem that remains, is when a user-defined mapping has the same name +as a node name, and has more than one argument (or doesn't accept a single old +node). This is probably something we can cope with, simply rejecting programs +that trigger this case, and request that the programmer uses different names for +the mapping and node. + +@subsection{Shadowing} + +We want to shadow the old @tc[city] type everywhere inside the graph +declaration. Otherwise this will lead to inconsistencies, inside an inner +definition for example. We do not want however the name of the nodes to leak +outside as definitions, as this would conflict with other graphs having the same +node names. + +@chunk[<*> + (begin)] diff --git a/graph-lib/graph/graph.lp2.rkt b/graph-lib/graph/graph.lp2.rkt index afb3678d..763aed7a 100644 --- a/graph-lib/graph/graph.lp2.rkt +++ b/graph-lib/graph/graph.lp2.rkt @@ -325,7 +325,7 @@ arguments, tagged with the @tc[node]'s name): @; TODO: maybe replace node types with placeholder types @chunk[ - (struct (A) node/placeholder-struct ([f : A])) + (struct (A) node/placeholder-struct ([f : A]) #:transparent) (define-type node/placeholder-type (node/placeholder-struct (List param-type …)))] @@ -351,7 +351,7 @@ indicates at which index in the queue's results the successor can be found. @; TODO: use a type-expander here, instead of a template metafunction. @CHUNK[ - (struct node/index-type ([i : Index])) + (struct node/index-type ([i : Index]) #:transparent) (define-type node/with-indices-type (List 'node/with-indices-tag …)) diff --git a/graph-lib/type-expander/type-expander.lp2.rkt b/graph-lib/type-expander/type-expander.lp2.rkt index f08a4ac2..c5067ae2 100644 --- a/graph-lib/type-expander/type-expander.lp2.rkt +++ b/graph-lib/type-expander/type-expander.lp2.rkt @@ -151,7 +151,9 @@ else. #,(expand-type #'T (bind-type-vars #'(TVar ...) env)))] [((~literal Rec) R:id T:expr) #`(Rec R #,(expand-type #'T (bind-type-vars #'(R) env)))] - [((~literal Let) [V:id E:id] T:expr) + [((~datum Let) [V:id E:id] T:expr);; TODO: ~literal instead of ~datum + ;; TODO: ~commit when we find Let, so that syntax errors are not + ;; interpreted as an arbitrary call. ;; TODO : for now we only allow aliasing (which means E is an id), ;; not on-the-fly declaration of type expanders. This would require ;; us to (expand) them.