Reputation: 1965
Following this tutorial, I have the following code:
{-# LANGUAGE DataKinds, TypeFamilies #-}
data Nat = Z | S Nat
type family Plus (n :: Nat) (m :: Nat) :: Nat
type instance Plus Z m = m
type instance Plus (S n) m = S (Plus n m)
So far so good. I know that DataKinds
does auto promotion of types to kinds, and TypeFamilies
enable type-level functions.
Then I add:
type family Mul (m :: Nat) (n :: Nat) :: Nat
type instance Mul Z m = Z
type instance Mul (S n) m = Plus m (Mul n m)
Compiling gives me:
Nested type family application
in the type family application: Plus m (Mul n m)
(Use UndecidableInstances to permit this)
In the type instance declaration for ‘Mul’
The name of this extension is scary. Some people say to avoid it, and explanations of this extension I've tried reading do not make sense to me.
Looking for a bit of help in understanding the error, why it is showing up in this example, what is the meaning of undecidable in this context and in what instances this extension is as scary as it sounds?
Upvotes: 3
Views: 199
Reputation: 120711
UndecidableInstance
is ok to use. Granted, you should always consider a moment whether something is really needed, or it would be more idiomatic to choose another path.
Luke makes the point that UndecidableInstances
should not be used to implement superclassing. Well, that's true, but it doesn't really have much to do with undecidable instances – superclassing is just always a hack and pretty much backwards to the way abstractions should be designed.
It is IME not true, at least not anymore, that superclassing is the most common use of UndecidableInstances
. There are plenty of things you can do with this extension enabled but not without. Most certainly, it can't be possible to impose a halting-proof condition if you actually want to do Turing-complete dependent typing at compile-time, which is basically what all the DataKinds fuzz is heading to.
So, don't worry too much; indeed you probably won't get around UndecidableInstances
. As said by András Kovács, this extension is perfectly safe WRT correctness, the worst that can happen is a loop / error.
If that's too scary for your tastes, use a language that's designed up front as dependently-typed and total, i.e. Agda.
Upvotes: 2
Reputation: 30103
It's not really scary. It disables the (rather weak) GHC checks that try to ascertain that type family and instance definitions terminate. So, its worst case behavior is non-terminating type checking. However, most often we get error messages instead of actual looping, because loops often exceed stack limits in the type checker. We don't get unsafety, incoherence or any other property that may adversely affect program robustness or inference.
"Undecidable" is probably a stronger expression than warranted, since it only denotes that GHC can't decide whether a definition is terminating. In many cases it's GHC that's too weak for the job, and other compilers for dependent languages would be able to check termination without problem. With GHC, UndecidableInstances
is often required for obviously terminating definitions too, in which cases its usage is quite justified.
Upvotes: 2