***  x: opt(integer)  ***

We know what this means.  x can be an integer or something like "None".

Can we use x in operations that take integers?
For example,
  x + 3

If "yes", then the type of this addition is also opt(integer).
So the result type of an addition could be either integer or opt(integer),
depending on the types of the arguments.

If "no", then type checking should give an error.

Which of these alternatives do we want for Syntheto?

In either case, we will want a way to create values of opt(T)
(where T is a placeholder for a declared type)
and a way to checks values of opt(T) for T or None.

The easier thing to do for now is "no".

What are the relevant language constructs?

(1) a keyword "None".  This is a polymorphic type value.
    It will only be allowed in contexts where it is clear
    what its type is.  For example,
      let (x: opt(T) = None) ..
      ..
      if x == None then ..
      ..
      return None
    For added fun, you can do things like
      let (x: set(opt(integer)) = {-1,0,1,None}) ..

(2) make-option(x), where x is any value of any type T.
    The type of this expression is opt(T).
    This type must have already been explicitly declared somewhere,
    at least for now.

(3) To check if a value x:opt(T) is None, simply compare equality:
    if x == None then ..
    if x != None then ..
    Alternatively, we could have something like isNone(..)

(4) To get the underlying value, of x:opt(t):
    option-value(x).  This would be a type error if x is None,
    so we have to guard it.  The recommendation
    to users is to do
       if x != None then .. option-value(x) ..

Opinions?


[AC]----------

I think it would be best to view this as a parameterized variant type

variant opt<T> {
    some {get : T},
    none {}
}

(In the future, we may want to extend Syntheto with user-defined polymorphic
types, so the above could be valid Syntheto syntax.)

Then we use whichever mechanisms and constructs we have (still under design) for
values of variant types.

opt.none would have to be polymorphic, as you note -- in the same way that
seq.empty or set.empty are (assuming we use those names for the empty list and
set). So maybe Syntheto will need to do some Hindley-Milner-style type
inference.

Here are some ideas, which would apply to other variant types too.

Some construct ideas:

opt.none -- obvious

opt.some { get = ... } -- long form, required(?) when there are 2+ fields
opt.some(...) -- short form -- ? allowed only when there is just 1 field

x.none? -- test if x:opt<T> is none -- return boolean
x.some? -- test if x:opt<T> is some -- return boolean

x.some!get -- return the contained value of type T -- must prove x.some? here
x.some -- ? same as above, short form, allowed only when there is just 1 field

Consider a variant types with more than one field, for comparison:

variant expr {
    var {name : string},
    const {value : integer},
    add {left : expr, right :expr}
}

expr.var("x")
expr.const(12)
expr.add {left = e1, right = e2}
expr.add (e1, e2) -- short form -- ? allow also with 2+ fields

expr.var?
expr.const?
expr.add?

expr.var!name
expr.const!value
expr.add!left
expr.add!right

[AC]----------

[EM]----------

Thanks for the ideas.

I think it could be confusing to use Java instance variable
syntax without the Java capitalization conventions.
I think it is confusing to have both x.some and opt.some where
x is a value and opt is a typename.

I think we need to label each idea as to whether this is with the
current shallow macros or future work.

When I do something like this in Syntheto:
  let (x: opt(integer) = least-modular-square-root(n,p)) ...
currently (or nearly), it will make a type like this:

(fty::deftagsum OPT[INT]
   (:some ((get int)))
   (:none ()))

How can I do the following, in Syntheto code,
and what does it turn into in shallow embedding macros?

  (1) test this x for whether it is none?

  (2) test this x for whether it is some(..)?

  (3) test this x for whether it is some(3)?

  (4) Presuming I have validated it is some(..),
      how do I extract the value?

Then, in the definition of least-modular-square-root,
how can I (and what does it turn into):

  (5) create and return the value of none,

  (6) create and return a value like some(3)

In ACL2 code currently, to create a value:
  (N) note, numbers refer to the same numbers above
  (5) (opt[int]-none)
  (6) (opt[int]-some 3)

to test a value x:
  (1) (eq :NONE (opt[int]-kind x))
  (2) (eq :SOME (opt[int]-kind x))
  (3) (and (eq :SOME (opt[int]-kind x)) (= 3 (opt[int]-some->get x)))
and to extract the integer component, after validating it:
  (4) (opt[int]-some->get x)
