Answer posted by gelisam on the dependent_types subreddit

There are two aspects to your question.

The type of a variable changes as it is mutated Ambiguous type after a conditional block

An assignment which changes the type of the assigned variable is called a "strong update". If you really want your language to support those, be warned that tracking those type changes will make your type system much more complicated, because your type judgements will need to keep track of the code regions inside which your hypotheses and conclusions are valid.

One such type system is described in Stefan Monnier's paper Statically tracking state with Typed Regions. It's been a while since I have learned about this, but the paper seems to describe a language which has statements for strong update, and yet supports very powerful dependent types, namely the calculus of inductive constructions.

Given the kind of language you are trying to implement, however, you probably can't afford such an increase in your complexity budget. So here's a much simpler solution: use ordinary assignments instead of strong updates!

I think I understand why you think you need strong updates, but you probably don't. Consider the following Java snippet:

Parent p = new Child1(); p = new Child2(); if (condition) { p = new Child3(); }

The runtime type of p changes at each assignment, but since Java lacks strong updates, the compile-time type of p remains Parent. There is a tradeoff in the choice of p : a type such as Child1 would have been more precise, and would have allowed more methods to be called on p , but would only allow future assignments to replace p with another value of type Child1 or a subclass thereof. The type Parent allows more flexible assignments, but restricts the available methods to those of Parent .

Similarly, when choosing a type for a mutable variable xs in your dependently-typed language, users could have to choose between a precise type such as ist<int,3> and an imprecise type such as Σ n:Nat. List<int, n> , that is, a dependent pair indicating that the int part of List<int,n> is fixed but the n part can be changed by mutations.

With the former type, some methods would be available, such as those which require the list to be non-empty for example, while other methods would be unavailable, namely those which change the length of the list. With the latter type, only methods which work regardless of the current n could be called, but this time it would be legal to change the length of the list. In particular, it would be legal to change the length of the list inside a conditional block, because the code after the block is already unable to call methods which expect a particular length.

What I am trying to say is that adding mutability to a language with dependent types does not necessarily lead to also adding strong updates, and that a language with the first two features but not the third would be simpler and more familiar than a language with strong updates.