How to tell if a list is infinite?

后端 未结 6 493
迷失自我
迷失自我 2020-12-15 03:07

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.

6条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-15 03:55

    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 ;)

提交回复
热议问题