variables {δ σ : Type}

def foo1 : state_t δ (state_t σ id) σ :=
monad_lift (get : state_t σ id σ)
/-
In Lean3, we used to use the quasi-pattern approximation during elaboration.
The example above demonstrates why it produces counterintuitive behavior.
We have the `monad-lift` application:

@monad_lift ?m ?n ?c ?α (get : state_t σ id σ) : ?n ?α

It produces the following unification problem when we process the expected type:

?n ?α =?= state_t δ (state_t σ id) σ
==> (approximate using first-order unification)
?n := state_t δ (state_t σ id)
?α := σ

Then, we need to solve:

?m ?α =?= state_t σ id σ
==> instantiate metavars
?m σ =?= state_t σ id σ
==> (approximate since it is a quasi-pattern unification constraint)
?m := λ σ, state_t σ id σ

Remark: the constraint is not a Milner pattern because σ is in
the local context of `?m`. We are ignoring the other possible solutions:
?m := λ σ', state_t σ id σ
?m := λ σ', state_t σ' id σ
?m := λ σ', state_t σ id σ'

We need the quasi-pattern approximation for elaborating recursors.
One option is to enable this kind of approximation only when
elaborating recursors and executing induction-like tactics.

If we had use first-order unification, then we would have produced
the right answer: `?m := state_t σ id`

Haskell would work on this example since it always uses
first-order unification.
-/

def foo2 : state_t δ (state_t σ id) σ :=
do s : σ  ← monad_lift (get : state_t σ id σ),
   return s
