this post was submitted on 15 Apr 2024
1 points (100.0% liked)

Haskell

6 readers
1 users here now

**The Haskell programming language community.** Daily news and info about all things Haskell related: practical stuff, theory, types, libraries, jobs, patches, releases, events and conferences and more... ### Links - Get Started with Haskell

founded 1 year ago
 

This fuses:

[(x, y) | x <- [0 .. 10], y <- [0 .. 10]]

But this does not:

liftA2 (,) [0 .. 10] [0 .. 10]

Because the latter inlines to:

let xs = [0..10]; ys = [0..10] in xs >>= \x -> ys >>= \y -> (x, y)

And GHC is afraid to push the ys binding into the \x -> ... lambda because that might duplicate the work of evaluating [0..10]. Even though in the end fusing everything would be beneficial.

See: https://gitlab.haskell.org/ghc/ghc/-/issues/24663

top 1 comments
sorted by: hot top controversial new old
[–] [email protected] 1 points 5 months ago

Stream fusion does work:

data Stream a = forall s. Stream !(s -> Step s a) !s
data Step s a = Yield a !s | Skip !s | Done

data Tup a b = Tup !a !b

cartesianProduct :: Stream a -> Stream b -> Stream (a, b)
cartesianProduct (Stream step1 s01) (Stream step2 s02) = Stream step' s' where
  s' = Tup s01 s02
  step' (Tup s1 s2) =
    case step1 s1 of
      Yield x s1' ->
        case step2 s2 of
          Yield y s2' -> Yield (x, y) (Tup s1 s2')
          Skip s2' -> Skip (Tup s1 s2')
          Done -> Skip (Tup s1' s02)
      Skip s1' -> Skip (Tup s1' s2)
      Done -> Done

eft :: Int -> Int -> Stream Int
eft x y = Stream step x where
  step s
    | s > y = Done
    | otherwise = Yield s (s + 1)

fooS :: Stream (Int, Int)
fooS = cartesianProduct (eft 0 10) (eft 0 10)

toList :: Stream a -> [a]
toList (Stream step s0) = go s0 where
  go !s =
    case step s of
      Yield x s' -> x : go s'
      Skip s' -> go s'
      Done -> []

foo :: [(Int,Int)]
foo = toList fooS