3

I am new to Haskell, and I have the following code:

second (x:y:xs) = y : second xs  -- returns every second element of a list
second _ = []

xs = [1,2,3,4] ++ second xs

I expect xs to be evaluated to [1,2,3,4,2,4,4], because that is the fixed point, i.e. [1,2,3,4,2,4,4] == [1,2,3,4] ++ second [1,2,3,4,2,4,4].

However, when I try to evaluate xs in GHCi, I get

Prelude> xs
[1,2,3,4,2,4,4

but it doesn't stop computing.

Could anyone explain why this doesn't stop, and is there a simple way to make the computation stop and return [1,2,3,4,2,4,4]?

2 Answers 2

5

[1,2,3,4,2,4,4] ++ [] is a fixed point of ([1,2,3,4] ++) . second, but it is not the least fixed point.

That is [1,2,3,4,2,4,4] ++ undefined, which is smaller. It is smaller in the sense that it is less defined than the first one.

The first one is more defined in that it has its 7th tail defined whereas the second's 7th tail is undefined.

That's the general outlook. But specifically we can follow the computations step by step, naming all the interim values and expanding the definition, and we will find out that the "get" point on the result catches up with the "put" point which is initially farther ahead, but the "get" point is twice faster than "put". So that when they meet there's nothing there yet that we've put there which we could get.

Thus the computation gets stuck, waiting for something to appear where there's nothing, and there's nothing that could put anything there.

The only way to avoid this is to put take 7 on top of it.

On an unrelated note I'd call that function seconds, not second.


So it goes like this:

xs = [1,2,3,4] ++ second xs

              a b c         (_:a:p) = xs = [1,2,3,4]++second xs
              ↓ ↓ ↓         (_:b:q) = p  = [    3,4,2]++second p
   =  1 2 3 4 2 4 4         (_:c:r) = q  = [        2,4]++second q
        ↓   ↓   ↓   ↓                 r  = [            4]++second r
        a   b   c    
      ↑   ↑   ↑   ↑                  r = drop 2 q = second q = 
      xs  p   q   r                    = second (2:4:r) = 4:second r

head r is well defined, it is

r = drop 2 q = second q 
             = second (_:c:r) 
             = c:second r
head r = c = 4
tail r = 

but then we need to find tail r. Is it a (:) data node? Is it []?

       = tail (c:second r)
       = second r               -- (1)
       = second (c:second r)
       = case (c:second r) of
           (x:y:z) -> y:second z
           []      -> []
       = case (second r) of     -- (2)
           (y:z) -> y:second z

and so to find out what second r ( (1) ) is, we need to find out what second r ( (2) ) is.

We're stuck.

Sign up to request clarification or add additional context in comments.

26 Comments

A small demonstration gist.github.com/pedrofurla/ffc61559d7a4fba74d4c42c6da0c3832 I was going to add to an answer, but to be complete it would need to go through evaluating everything manual. It would tedious.
take 7 is not the only choice. You could also define your own fixpoint operator that worked by repeated application and equality checking, e.g. fixEq :: Eq a => (a -> a) -> (a -> a); fixEq f guess | guess' == guess = guess | otherwise = fixEq f guess' where guess' = f guess.
@WillNess Call it with any (finite, fully-defined) list as guess, e.g. fixEq (([1,2,3,4]++) . second) [].
@eivour Err... yes! My math is off in both of my last comments. Here is a corrected one: the current implementation is O(n*log(n)). Brent's algorithm has the same asymptotic complexity (but is probably faster anyway). No generic fixpoint algorithm can do asymptotically better. A specialized algorithm (rather than generic one) can get you to O(n).
@WillNess Like fix, fixEq only promises to return a fixpoint, and not necessarily the fixpoint you have in mind.
|
4

Another more intuitive way to see it is that the [] (end of the list) has to come from somewhere. The second function will only return [] once it encounters a [] in the input, so you end up with a chicken and egg situation. In this case the [] is never produced and therefore the computation can never stop.

6 Comments

second returns [] whenever there are less than two elements in the list, so repeated application of second eventually returns [].
Not really , eg xs = [] ++ second xs.
@eivour For the input to have less than two inputs, it must already have a [], as even a singleton list [x] is actually x:[] behind the scenes. And that [] has to come from somewhere -- the argument "second produces [] because it sees a [] that came from second" leads to the question, "but why did a [] come from second"... an infinite regress. For the [] to come from somewhere in a finite argument, it has to come from something other than a recursive call to second.
@eivour ...and to be explicit about it, while Haskell does infinite values just fine, it doesn't do infinite arguments at all (here I use "arguments" in the sense of my previous comment, not in the sense of "function arguments").
@DanielWagner turns out the culprit here is r = 4:second r as the last "cons cell". I've edited my answer with the specifics. :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.