While working over the weekend with Chris Casinghino and Vilhelm Sjöberg at U. Penn. and Garrin Kimmell here at Iowa on the Trellys core language design and prototype implementation, I had occasion to recall a very tricky way in which too liberal datatype definitions can lead to inconsistency, even if we do not assume a terminating recursor for all datatypes. I believe I learned this trick from Benjamin Wack, although at this point I am not sure. The code (in our new Trellys core-language syntax) is here:

data Empty -> Type 0 where {} data Loop -> Type 0 where CLoop : (f:((b:Loop) => Empty)) => Loop prog loop_step : (b : Loop) => Empty loop_step = \ b . case b [beq] of CLoop f -> f b prog loop : Empty loop = loop_step (CLoop loop_step)

Notice that this example does not use any explicit recursion (rec or recnat in Trellys), but yet manages to define empty of type Empty. It can only do that as indeed it does, by writing an infinite loop. The trick here is to say that the constructor CLoop for the Loop type takes in a function from Loop to Empty as its argument. Terminating type theories like Coq do not allow this, nor does the logical fragment of the Trellys core language. Constructors that take in arguments whose types mention the type being constructed in a negative position must take those arguments in as programmatic arguments, rather than logical arguments. Trellys distinguishes programmatic terms from logical ones, while allowing the two to interact. The above example is programmatic, as shown by the use of the thick arrow in several places, and the “prog” keyword for loop_step and loop. The thick arrow means that the input may be programmatic. A thin arrow for function types means that the argument can only be logical, not programmatic. If we replace the thick arrows above with thin ones and drop “prog”, the example will not type check (at least, it won’t as soon as Chris and/or Vilhelm implement the positivity check :-)).

Anyhow, I thought it was worthwhile to share this example, since it shows that datatypes which are fine for programming can be dangerous for logic, even if we do not assume a terminating recursor for them.