/StreamFusionReassoc.hs

http://github.com/batterseapower/haskell-kata · Haskell · 89 lines · 44 code · 17 blank · 28 comment · 4 complexity · d6ff5d7c9e0f9a6aace1ae009ad3027e MD5 · raw file

  1. {-# LANGUAGE ExistentialQuantification, BangPatterns, TypeOperators #-}
  2. import Prelude hiding (enumFromTo, concatMap, replicate)
  3. data Stream a = forall s. Stream !(s -> Step a s) -- a stepper function
  4. !s -- an initial state
  5. -- | A stream step.
  6. --
  7. -- A step either ends a stream, skips a value, or yields a value
  8. --
  9. data Step a s = Yield a !s
  10. | Skip !s
  11. | Done
  12. -- | Construct an abstract stream from a list.
  13. stream :: [a] -> Stream a
  14. stream xs0 = Stream next xs0
  15. where
  16. {-# INLINE next #-}
  17. next [] = Done
  18. next (x:xs) = Yield x xs
  19. {-# INLINE [0] stream #-}
  20. -- | Flatten a stream back into a list.
  21. unstream :: Stream a -> [a]
  22. unstream (Stream next s0) = unfold_unstream s0
  23. where
  24. unfold_unstream !s = case next s of
  25. Done -> []
  26. Skip s' -> unfold_unstream s'
  27. Yield x s' -> x : unfold_unstream s'
  28. {-# INLINE [0] unstream #-}
  29. --
  30. -- /The/ stream fusion rule
  31. --
  32. {-# RULES
  33. "STREAM stream/unstream fusion" forall s.
  34. stream (unstream s) = s
  35. #-}
  36. {-# INLINE replicate #-}
  37. replicate n x = unstream (replicateS n x)
  38. {-# INLINE [0] replicateS #-}
  39. replicateS :: Int -> a -> Stream a
  40. replicateS n x = Stream next n
  41. where
  42. {-# INLINE next #-}
  43. next !i | i <= 0 = Done
  44. | otherwise = Yield x (i-1)
  45. {-# INLINE enumFromTo #-}
  46. enumFromTo x y = unstream (enumFromToS x y)
  47. {-# INLINE [0] enumFromToS #-}
  48. enumFromToS x y = Stream step x
  49. where
  50. {-# INLINE step #-}
  51. step x | x <= y = Yield x (x + 1)
  52. | otherwise = Done
  53. data a :!: b = !a :!: !b
  54. {-# INLINE concatMap #-}
  55. concatMap f xs = unstream (concatMapS (stream . f) (stream xs))
  56. {-# INLINE [0] concatMapS #-}
  57. concatMapS :: (a -> Stream b) -> Stream a -> Stream b
  58. concatMapS f (Stream next0 s0) = Stream next (s0 :!: Nothing)
  59. where
  60. {-# INLINE next #-}
  61. next (s :!: Nothing) = case next0 s of
  62. Done -> Done
  63. Skip s' -> Skip (s' :!: Nothing)
  64. Yield x s' -> Skip (s' :!: Just (f x))
  65. next (s :!: Just (Stream g t)) = case g t of
  66. Done -> Skip (s :!: Nothing)
  67. Skip t' -> Skip (s :!: Just (Stream g t'))
  68. Yield x t' -> Yield x (s :!: Just (Stream g t'))
  69. -- [1,1,2,2,3,3,4,4,5,5,2,2,3,3,4,4,5,5,3,3,4,4,5,5,4,4,5,5,5,5]
  70. main = do
  71. print $ concatMap (\y -> replicate 2 y) (concatMap (\x -> enumFromTo x 5) (enumFromTo 1 (5 :: Int)))
  72. --print $ concatMap (\x -> concatMap (\y -> replicate 2 y) (enumFromTo x 5)) (enumFromTo 1 (5 :: Int))