Would the ability to detect cyclic lists in Haskell break any properties of the language?

后端 未结 2 598
慢半拍i
慢半拍i 2021-01-07 19:44

In Haskell, some lists are cyclic:

ones = 1 : ones

Others are not:

nums = [1..]

And then there are things

相关标签:
2条回答
  • 2021-01-07 20:01

    Would the existence of this function break any interesting properties of the language?

    Yes it would. It would break referential transparency (see also the Wikipedia article). A Haskell expression can be always replaced by its value. In other words, it depends only on the passed arguments and nothing else. If we had

    isCycle :: [a] -> Bool
    

    as you propose, expressions using it would not satisfy this property any more. They could depend on the internal memory representation of values. In consequence, other laws would be violated. For example the identity law for Functor

    fmap id === id
    

    would not hold any more: You'd be able to distinguish between ones and fmap id ones, as the latter would be acyclic. And compiler optimizations such as applying the above law would not longer preserve program properties.

    However another question would be having function

    isCycleIO :: [a] -> IO Bool
    

    as IO actions are allowed to examine and change anything.

    A pure solution could be to have a data type that internally distinguishes the two:

    import qualified Data.Foldable as F
    
    data SmartList a = Cyclic [a] | Acyclic [a]
    
    instance Functor SmartList where
        fmap f (Cyclic xs) = Cyclic (map f xs)
        fmap f (Acyclic xs) = Acyclic (map f xs)
    
    instance F.Foldable SmartList where
        foldr f z (Acyclic xs) = F.foldr f z xs
        foldr f _ (Cyclic xs) = let r = F.foldr f r xs in r
    

    Of course it wouldn't be able to recognize if a generic list is cyclic or not, but for many operations it'd be possible to preserve the knowledge of having Cyclic values.

    0 讨论(0)
  • 2021-01-07 20:05

    In the general case, no you can't identify a cyclic list. However if the list is being generated by an unfold operation then you can. Data.List contains this:

    unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
    

    The first argument is a function that takes a "state" argument of type "b" and may return an element of the list and a new state. The second argument is the initial state. "Nothing" means the list ends.

    If the state ever recurs then the list will repeat from the point of the last state. So if we instead use a different unfold function that returns a list of (a, b) pairs we can inspect the state corresponding to each element. If the same state is seen twice then the list is cyclic. Of course this assumes that the state is an instance of Eq or something.

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