2

For example

partitions [1,2,3] =
  [([],[1,2,3])
  ,([1],[2,3])
  ,([1,2],[3])
  ,([1,2,3],[])]
partitions :: [a] -> [([a], [a])]
partitions (x:xs) = ([], x:xs):[(x:ys, rs) | (ys, rs) <- partitions xs]

I wonder is it lazy solution. For example partitions [1..] is infinite. Additionally, take 5 $ partitions [1..] is also infinite. I think that obvious given the fact that result of this function is infinite. However, I am not sure if it is lazy, if I correctly understand laziness.

dfeuer
  • 48,079
  • 5
  • 63
  • 167
  • Haskell is lazy by default. You haven't added any bang or used any strict functions. So, yes your function is lazy. – Sibi Mar 09 '16 at 17:33
  • So, when function is not lazy ? –  Mar 09 '16 at 17:43
  • 1
    I think there is other better terminology for talking about the behavior of functions under lazy evaluation (like "codata"/"corecursion"). I don't think "lazy" is well-defined except as an evaluation strategy. – jberryman Mar 09 '16 at 17:58
  • 1
    What do you want `partitions []` to be? Your current code has no base case. I think you'd probably need to add *two* base cases to make it work the way you mean, unless you change the structure some. – dfeuer Mar 09 '16 at 19:19

1 Answers1

6

There are different degrees of laziness.

One might say that your function is strict since partitions undefined triggers an exception, but that would be too pedantic.

Chances are that by "lazy" you actually mean "it will produce a part the output after having accessed only a part of the input". Several degrees of laziness then arise, depending on how much input is needed for each part of the output.

In your case, the shape of the function is as follows:

foo [] = (some constant value)
foo (x:xs) = C expression1 ... expressionN

where C is a value constructor. More precisely, C = (:) and N=2. Since Haskell constructors are lazy (unless bang annotations are involved), the result of foo (x:xs) will always be non-bottom: consuming an element in the input list is enough to produce an element of the output list.

You might be confused by the output of partitions [1..] being an infinite list of pairs (xs, ys) where each ys is an infinite list. This makes the notion of laziness much more complex, since you might now wonder, e.g., "how much input is accessed for me to take the 100th pair of the output, and then access the 500th element of its second component?". Such questions are perfectly legitimate, and tricky to answer in general.

Still, your code will never demand the full input list to output a finite portion of the output. This makes it "lazy".


For completeness, let me show a non lazy function:

partitions (x:xs) = case partitions xs of
   []     -> expression0
   (y:ys) -> expression1 : expression2 

Above, the result of the recursive call is demanded before the head of the output list is produced. That will demand the whole input before any part of the output is generated.

chi
  • 111,837
  • 3
  • 133
  • 218
  • Are you sure the the above example is not lazy ? something like this works fine: http://lpaste.net/154356 – Sibi Mar 09 '16 at 18:50
  • @Sibi: As does `length [undefined]`. chi's partition is spine strict, not element strict. Your example would fail on `[1,2,3,4,5] ++ undefined` – Zeta Mar 09 '16 at 19:19
  • @Sibi It is lazy in the elements, but strict in the spine. Parametricity makes it impossible to write an element-strict function without using `seq` or similar things (as you already noticed). – chi Mar 09 '16 at 19:19
  • Thanks, that makes it clear. So, the only criteria for something to be called as lazy for it would be to have a non-strict spine ? – Sibi Mar 09 '16 at 19:49
  • 2
    @Sibi it'll depend on the specifics of the function in question. What matters is *productivity*: that a finite portion of output is created in a finite amount of "time". This obviously implies it'll be inspecting only a finite portion of its input. `partitions` inspect the whole of its input list before producing its first element, so if that list is infinite... etc. that's what it means to say that it's strict in its input list's spine, I gather. – Will Ness Mar 10 '16 at 08:49