The “option type can be specialised to its constituent type” still worries me…
Here is one problem, maybe fixable, but maybe indication that this rule just doesn't fit well with the system.
With this rule, we certainly want
true ~> opt true : opt bool
But also
true ~> opt true : opt bool
true ~> opt opt true : opt opt bool
true ~> opt opt opt true : opt opt opt bool
…
So what does this mean for
type FixOpt = opt FixOpt
true ~> ? : FixOpt
Let’s try to apply the rules (with a logical variable ?X for the result).
true ~> ?X : FixOpt
⇐ opt true ~> ?X : FixOpt
⇐ opt true ~> opt ?Y : FixOpt -- ?X = opt Y
⇐ true ~> ?Y : FixOpt
and we are in a loop. The solution ?X = opt ?X is not allowed (our values are inductive, not coinductive – I hope!). And since we understand _ ~> _ : _ to be inductive, we should get
not (∃x. true ~> x : FixOpt)
But by the rule about “failed parses at type opt turn into null`”, this implies
opt true ~> null : opt FixOpt
which (because opt FixOpt = Opt) implies
opt true ~> null : FixOpt
which implies
by the “subtype to constituent type” rules.
This is a blatant contradiction! This means our _ ~> _ : _ relation is inconsistent!
How can that happen? Don’t all inductively defined relations well-defined? No, they are only well-defined when the relation appears only in strictly positive positoins in the rules. And our rule
not (<v> ~> _ : <t>)
-------------------------
opt <v> ~> null : opt <t>
breaks that. This would also be a major headache when trying to put this into a theorem prover.
The way to deal with that there (e.g. Coq) would be to define the relation as a well-founded function on the v (we can’t use t because the types themselves are coinductive). In all rules, the antecents only mention subterm of v. And it also (nicely) matches a real implementation which would traverse v.
Well, all rules but his one:
<v> ≠ null
<v> ≠ (null : reserved)
<v> ≠ opt _
opt <v> ~> <v'> : opt <t>
-------------------------
<v> ~> <v'> : opt <t>
Maybe we can patch around this issue (e.g. requiring <t> ≠ opt <t>) … but I am leaning towards just dropping it. It makes the formalism cleaner (a good sign) and it makes the implementation easier to get right and secure (they otherwise have to guard themselves against an infinite loop upon true ~> ? : FixOpt).
Do we have any compelling reason/use case for this rule?
not (null <: <datatype>)
<datatype> <: <datatype'>
-----------------------------
<datatype> <: opt <datatype'>
The “option type can be specialised to its constituent type” still worries me…
Here is one problem, maybe fixable, but maybe indication that this rule just doesn't fit well with the system.
With this rule, we certainly want
But also
So what does this mean for
Let’s try to apply the rules (with a logical variable
?Xfor the result).and we are in a loop. The solution
?X = opt ?Xis not allowed (our values are inductive, not coinductive – I hope!). And since we understand_ ~> _ : _to be inductive, we should getBut by the rule about “failed parses at type
optturn into null`”, this implieswhich (because
opt FixOpt = Opt) implieswhich implies
by the “subtype to constituent type” rules.
This is a blatant contradiction! This means our
_ ~> _ : _relation is inconsistent!How can that happen? Don’t all inductively defined relations well-defined? No, they are only well-defined when the relation appears only in strictly positive positoins in the rules. And our rule
breaks that. This would also be a major headache when trying to put this into a theorem prover.
The way to deal with that there (e.g. Coq) would be to define the relation as a well-founded function on the
v(we can’t usetbecause the types themselves are coinductive). In all rules, the antecents only mention subterm ofv. And it also (nicely) matches a real implementation which would traversev.Well, all rules but his one:
Maybe we can patch around this issue (e.g. requiring
<t> ≠ opt <t>) … but I am leaning towards just dropping it. It makes the formalism cleaner (a good sign) and it makes the implementation easier to get right and secure (they otherwise have to guard themselves against an infinite loop upontrue ~> ? : FixOpt).Do we have any compelling reason/use case for this rule?