Why don't type synonyms permit recursion in Haskell?

前端 未结 2 1037
谎友^
谎友^ 2020-12-14 18:26

Can anyone explain why these both compile happily :

data A a b = A { a :: a, b :: b }
newtype B a = B (A a (B a))
newtype C = C (A Int C)

B

相关标签:
2条回答
  • 2020-12-14 18:51

    This has to do with Equi-recursive types versus iso-recursive types. Haskell implements recursive types using iso-recursive types, which require the programmer to tell the type-checker when type recursion is happening. The way you mark it is with a specific constructor, which a simple type-synonym doesn't allow you to have.

    Equi-recursive types allow the compiler to infer where recursion is happening, but it leads to a much more complicated type-checker and in some seemingly simple cases the problem is undecidable.

    If you'd like a good discussion of equi vs. iso recursive types, check out Benjamin Pierce's excellent Types and Programming Languages

    Short answer: because type synonyms don't introduce constructors, and haskell needs constructors to explicitly mark recursion at the type-level, you can't use recursive type synonyms.

    0 讨论(0)
  • 2020-12-14 18:51

    I will answer your first question and second questions.

    The type of B is the infinite type (A a (A a (A a (A a (...)))))

    The "type inference engine" could be designed to infer and handle infinite types. Unfortunately many errors (typographical or logical) by the programmer create code that fails to have the desired finite type and accidentally & unexpectedly has an infinite type. Right now the compiler rejects such code, which is nearly always what the programmer wants. Changing it to allow infinite types would create much more difficult to understand errors at compile time (at least as bad as C++ templates) and in rare cases you might make it compile and perform incorrectly at runtime.

    Is there any way to avoid dealing with that extra constructor X everywhere I want the type recursive?

    No. Haskell has chosen to allow recursive types only with explicit type constructors from data or newtype. These make the code more verbose but newtype should have little runtime penalty. It is a design decision.

    0 讨论(0)
提交回复
热议问题