Deforestation in Heliomorphism

Wikipedia writes about Hylomorphism :

In functional programming [...], a homomorphism is a recursive function corresponding to the composition of the anamorphism (which first builds a set of results; also known as “unfolding”) by a catamorphism (which then reduces these results to a final cost return). Merging these two recursive calculations into a single recursive template then avoids creating intermediate data structure . This is an example of deforestation, an optimization strategy program.

(in bold)

Using the recursion-schemes library I wrote a very simple hylomorphism:

import Data.Functor.Foldable
main :: IO ()
main = putStrLn $ show $ hylosum 1000

hylosum :: Int -> Int
hylosum end = hylo alg coalg 1
  where 
    -- Create list of Int from 1 to n
    coalg :: Int -> ListF Int Int
    coalg n 
       | n > end = Nil
       | otherwise = Cons n (n + 1)
    -- Sum up a list of Int's
    alg :: ListF Int Int -> Int
    alg Nil  =  0
    alg (Cons a x) = a + x

In the cabal file, I instructed GHC to optimize the code:

name:                Hylo
version:             0.1.0.0
synopsis:            Hylomorphisms and Deforestation        
build-type:          Simple
cabal-version:       >=1.10

executable             Hylo
  main-is:             Main.hs
  ghc-options:         -O2
  build-depends:       base >=4.10 && <4.11 , recursion-schemes      
  default-language:    Haskell2010

stackage lts-10.0 (GHC 8.2.2) stack build stack exec Hylo -- +RTS -s, :

500500
      84,016 bytes allocated in the heap
       3,408 bytes copied during GC
      44,504 bytes maximum residency (1 sample(s))
      25,128 bytes maximum slop
           2 MB total memory in use (0 MB lost due to fragmentation)

hylosum 1000 hylosum 1000000 ( 1000 ), :

500000500000
  16,664,864 bytes allocated in the heap
      16,928 bytes copied during GC
  15,756,232 bytes maximum residency (4 sample(s))
      29,224 bytes maximum slop
          18 MB total memory in use (0 MB lost due to fragmentation)

, 84 16,664 . 200 , . , GHC /, !

: ( 1 n), ( n 1), , .

: GHC ? , -? , ? , : , GHC ?

+6
1

, . ( Cons Nil):

h n | n > end = 0
    | otherwise = n + h (n+1)

, h (n+1) , n. n -, end.

, . , :

-- with BangPatterns
h n !acc | n > end = acc
         | otherwise = h (n+1) (n + acc)

The hylosumcall (+)comes in alg, and we replace it with the call to the continuation that will be created hylo.

alg :: ListF Int (Int -> Int) -> Int -> Int
alg Nil acc = acc
alg (Cons n go) !acc = go (n + acc)

With this, I see the 51kB constant allocated on the heap.

+10
source

All Articles