Is there a way to tell if a list in Haskell is infinite? The reason is that I don\'t want to apply functions such as length
to infinite lists.
We don't need to solve the Halting Problem to call 'length' safely. We just need to be conservative; accept everything that has a finiteness proof, reject everything that doesn't (including many finite lists). This is exactly what type systems are for, so we use the following type (t is our element type, which we ignore):
terminatingLength :: (Finite a) => a t -> Int
terminatingLength = length . toList
The Finite class will only contains finite lists, so the type-checker will ensure we have a finite argument. membership of Finite will be our proof of finiteness. The "toList" function just turns Finite values into regular Haskell lists:
class Finite a where
toList :: a t -> [t]
Now what are our instances? We know that empty lists are finite, so we make a datatype to represent them:
-- Type-level version of "[]"
data Nil a = Nil
instance Finite Nil where
toList Nil = []
If we 'cons' an element on to a finite list, we get a finite list (eg. "x:xs" is finite if "xs" is finite):
-- Type-level version of ":"
data Cons v a = Cons a (v a)
-- A finite tail implies a finite Cons
instance (Finite a) => Finite (Cons a) where
toList (Cons h t) = h : toList t -- Simple tail recursion
Anyone calling our terminatingLength function must now prove that their list is finite, otherwise their code won't compile. This hasn't removed the Halting Problem issue, but we have shifted it to compile-time rather than run-time. The compiler may hang while trying to determine membership of Finite, but that's better than having a production program hang when it's given some unexpected data.
A word of caution: Haskell's 'ad-hoc' polymorphism allows pretty much arbitrary instances of Finite to be declared at other points in the code, and terminatingLength will accept these as finiteness proofs even if they're not. This isn't too bad though; if someone's trying to bypass the safety mechanisms of your code, they get the errors they deserve ;)