[Haskell-cafe] about Haskell code written to be "too smart"
Dan Weston
westondan at imageworks.com
Wed Mar 25 15:40:08 EDT 2009
So to be clear with the terminology:
inductive = good consumer?
coinductive = good producer?
So fusion should be possible (automatically? or do I need a GHC rule?) with
inductive . coinductive
Or have I bungled it?
Dan
wren ng thornton wrote:
> Thomas Hartman wrote:
>> sorry, wrong function.
>>
>> should be
>>
>> partitions [] xs = []
>> partitions (n:parts) xs =
>> let (beg,end) = splitAt n xs
>> in beg : ( case end of
>> [] -> []
>> xs -> partitions parts xs)
>
>
> It's not tail recursive, FWIW. The recursive expression has (:) as it's
> head before it hits `partitions`. It is however nicely coinductive,
> which has other good properties.
>
> We could make it tail-recursive easily,
>
> partitions = go id
> where
> go k [] xs = k []
> go k (n:ns) xs =
> let (beg,end) = splitAt n xs
> k' = k . (beg:)
> in case end of
> [] -> k' []
> xs' -> go k' ns xs'
>
> (Note how this version has `go` as the head of the recursive expression.)
>
> ...however this version has different strictness properties. In
> particular, let both input lists be infinite (and take a finite portion
> of the result). The original version works fine because it gives a
> little bit of output (beg:) at each step of the recursion ---which is
> all "coinductive" means. The tail-recursive version hits _|_ however,
> because we've delayed giving any input (k []) until one of the two lists
> hits [] ---we've tried doing induction on co-data and so we hit an
> infinite loop.
>
> This dichotomy between coinduction and tail-recursion is quite common.
> It's another example of the recently discussed problem of defining foldr
> in terms of foldl. Whether the termination differences matter depends on
> how the function is to be used.
>
>
> Another nice property of coinduction is that it means we can do
> build/fold fusion easily:
>
> partitions = \ns xs -> build (\cons nil -> go cons nil ns xs)
> where
> go cons nil = go'
> where
> go' [] xs = nil
> go' (n:ns) xs =
> let (beg,end) = splitAt n xs
> in beg `cons` case end of
> [] -> nil
> xs' -> go' ns xs'
>
> By using the GHC.Exts.build wrapper the fusion rules will automatically
> apply. The second wrapper, go, is just so that the worker, go', doesn't
> need to pass cons and nil down through the recursion.
>
More information about the Haskell-Cafe
mailing list