Skip to content

TM012 Constructor Syntax

Joe Politz edited this page Dec 13, 2013 · 1 revision

Constructor Syntax

Pyret only has syntactic support for creating a single special constructed datatype: lists. Going forward, Pyret should have usable syntax for supporting other datatypes, including sets, dictionaries, bags, matrices, arrays, and more. This myriad of constructors should not be baked into the language. Programmers should be able to define their own datatype constructors, and use the same pleasant syntax that Pyret provides for its built-in datatypes.

This proposal involves two major changes to Pyret syntax. First, the s-list production will be removed:

s-list: "[" binop-expr "," ... binop-expr "]" | "[" "]"

will no longer parse, and we'll remove s-list from the abstract syntax.

Second, a new syntactic form will be introduced:

s-constructor: "[" binop-expr ":" binop-expr "," ... binop-expr "]"
             | "[" binop-expr ":" "]"

In abstract syntax, this form will be:

s-constructor(constructor-expr :: Expr, element-exprs :: List<Expr>)

Desugaring

Option 1: It will desugar to:

<constructor-expr>(make-const-generator(<element-expr>, ...))

The definition of make-generator is left undecided in this proposal. Open questions include:

  1. Does the generator eagerly evaluate the expressions?
  2. Is the generator a built in concept in Pyret, provided by the runtime?

The generator here ought to optimize for performance and simplicity. It doesn't need to be early-student-facing, since library authors will mostly be defining datatypes.

Option 2: It will desugar to:

tmp = <constructor-expr>
tmp.add(<element-expr>, tmp.add(..., tmp.base))

This is a bit more restrictive in the kinds of constructors that can be created.

Other questions:

  1. What is the constructor's relationship to the annotation? If these uses end up with instantiated parametric types, it's odd to mix a lowercase constructor with uppercase annotations (see list/set mixtures below).
  2. If we go with the add/base option, it's less obvious how to do this for, e.g. arrays and matrices. Perhaps the number of elements needs be passed to the constructor as well?

Examples

lst1 = [ list<Number> : 1, 2, 3 ]

lst2 = [ list : 1, 2, 3 ]

setlist = [ list<Set<Number>>:
            [ set<Number> : 1, 2, 3, 3],
            [ set<Number> : 3, 2, 1] ]

emptylistnum = [ list<Number>: ]

emptylist = [ list : ]

emptyset = [ set : ]

idset = [ set : "a", "b", "c" ]

bindingset = [ set : binding("a", numV(5)), binding("b", numV(10)) ]

pointset = [ set : { x: 22, y: 46 }, { x: -2, y: -4 } ]

dict1 = [ dict : "variable1", numV(10), "var2", closV(bindingset, idE("x")) ]

dict1a = [ dict :
            "variable1", numV(10),
            "var2", closV(bindingset, idE("x")) ]

dict2 = [ dict :
          [ kv : "variable1", numV(10) ],
          [ kv : "var2", closV(bindingset, idE("x")) ] ]

id3 = [ matrix : [ row : 1, 0, 0 ],
                 [ row : 0, 1, 0 ],
                 [ row : 0, 0, 1 ] ]

id3a = [ matrix(3, 3) : 1, 0, 0, 0, 1, 0, 0, 0, 1 ]

id3b = [ matrix(3, 3) :
          1, 0, 0,
          0, 1, 0,
          0, 0, 1 ]

memory = [ array :
  "pair",     3,          5,         "num",     1 ,        "clos",    idE("x"),  1,         "x",       0,
  "pair",     0,          3,         "num",     72,        0     ,    0       ,  0,         0  ,       0,
  0     ,     0,          0,         0    ,     0 ,        0     ,    0       ,  0,         0  ,       0,
  0     ,     0,          0,         0    ,     0 ,        0     ,    0       ,  0,         0  ,       0
]

An alternative to colons

Instead of ":", we could use the keyword "of":

lst1 = [ list of 1, 2, 3 ]

lst2 = [ list<Number> of 1, 2, 3 ]

emptyset = [ set of ]

idset = [ set of "a", "b", "c" ]

bindingset = [ set of binding("a", numV(5)), binding("b", numV(10)) ]

pointset = [ set of { x: 22, y: 46 }, { x: -2, y: -4 } ]

id3 = [ matrix of [ row of 1, 0, 0 ],
                  [ row of 0, 1, 0 ],
                  [ row of 0, 0, 1 ] ]

We could also consider adding a colon after "of":

lst1 = [ list of: 1, 2, 3 ]

We could also consider "|":

lst1 = [ list | 1, 2, 3 ]

lst2 = [ list<Number> | 1, 2, 3 ]

emptyset = [ set | ]

idset = [ set | "a", "b", "c" ]

bindingset = [ set | binding("a", numV(5)), binding("b", numV(10)) ]

pointset = [ set | { x: 22, y: 46 }, { x: -2, y: -4 } ]

id3 = [ matrix | [ row | 1, 0, 0 ],
                 [ row | 0, 1, 0 ],
                 [ row | 0, 0, 1 ] ]

Notes for typing

Since this syntactic form creates collections, it will necessarily often be parametric. The right place to put the parametric instantiation is on the constructor-expr, for example:

[ list<Number> : 1, 2, 3 ]

This instantiation may be inferred, but the desugaring to an application in only makes sense if the constructor is first instantiated.