Search code examples
haskellrecursive-datastructuresalgebraic-data-typesy-combinatoragda

Why inductive datatypes forbid types like `data Bad a = C (Bad a -> a)` where the type recursion occurs in front of ->?


Agda manual on Inductive Data Types and Pattern Matching states:

To ensure normalisation, inductive occurrences must appear in strictly positive positions. For instance, the following datatype is not allowed:

data Bad : Set where
  bad : (Bad → Bad) → Bad

since there is a negative occurrence of Bad in the argument to the constructor.

Why is this requirement necessary for inductive data types?


Solution

  • The data type you gave is special in that it is an embedding of the untyped lambda calculus.

    data Bad : Set where
      bad : (Bad → Bad) → Bad
    
    unbad : Bad → (Bad → Bad)
    unbad (bad f) = f
    

    Let's see how. Recall, the untyped lambda calculus has these terms:

     e := x | \x. e | e e'
    

    We can define a translation [[e]] from untyped lambda calculus terms to Agda terms of type Bad (though not in Agda):

    [[x]]     = x
    [[\x. e]] = bad (\x -> [[e]])
    [[e e']]  = unbad [[e]] [[e']]
    

    Now you can use your favorite non-terminating untyped lambda term to produce a non-terminating term of type Bad. For example, we could translate (\x. x x) (\x. x x) to the non-terminating expression of type Bad below:

    unbad (bad (\x -> unbad x x)) (bad (\x -> unbad x x))
    

    Although the type happened to be a particularly convenient form for this argument, it can be generalized with a bit of work to any data type with negative occurrences of recursion.