Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: odd difference between compiled vs interpreted functions which print concatenated infinite lists

I'm just exploring Haskell for fun, and to learn about the language. I thought the following behavior was interesting, and I can't find the reason why this happens.

This is an often quoted piece of Haskell code which keeps calculating pi until interrupted, slightly modified to give a concatenated list of chars instead of a list of integers:

main :: IO ()
main =  do putStrLn pi'

pi' :: [Char]
pi' = concat . map show $ g(1,0,1,1,3,3) where
   g(q,r,t,k,n,l) =
      if 4*q+r-t<n*t
      then n : g(10*q,10*(r-n*t),t,k,div(10*(3*q+r))t-10*n,l)
      else g(q*k,(2*q+r)*l,t*l,k+1,div(q*(7*k+2)+r*l)(t*l),l+2)

If I run it from prelude, it starts concatenating a string resembling the digits of pi:

λ> putStrLn pi' 31415926535897932384626433832795028841971 ...etc

Works as expected, it instantly starts spewing out digits.

Now this is a piece of code I just quickly wrote which has the same structure. It's completely useless from a mathematical point of view, I was just messing around to find out how Haskell works. The operations are much simpler, but it does have the same type, and so does the sub function (except for the smaller tuple).

main :: IO ()
main =  do putStrLn func

func :: [Char]
func = concat . map show $ h(1,2,1) where
   h(a,b,c) =
     if a <= 1000
     then a : h((div a 1)+2*b,b,1)
     else h(b,div (b-3) (-1),div a a)

Same type of result from prelude:

λ> putStrLn func 1591317212529333741454953576165697377818589 ...etc

Works as expected, although much faster than the pi function of course, because the calculations are less complex.

Now for the part which confuses me:

If I compile: ghc pi.hs, and run my program: ./pi, the output stays blank forever, until I send an interrupt signal. At that moment, the whole calculated string of pi is instantly displayed. It doesn't "stream" the output into stdout, like GHCI does. OK, I know they don't always behave in the same way.

But next I run: ghc func.hs, and run my program: ./func... and it immediately starts printing the list of characters.

Where does this difference come from? I thought it might be because my stupid useless little function is (eventually) repeating, so the compiler can "predict" the output better?

Or is there another fundamental difference between the way the functions work? Or am I doing something utterly stupid?

Solution / Answer

Provided by Thomas & Daniel below, I was:

  1. Impatient. Large chunks eventually show up with the pi function, it's just a bit slow on my simple old coding machine.
  2. Not handling buffering in any way.

So after rewriting the main function:

import System.IO

main :: IO ()
main =  do hSetBuffering stdout NoBuffering
           putStrLn pi'

It was fixed!

like image 548
okdewit Avatar asked Oct 31 '22 09:10

okdewit


1 Answers

Provided by Thomas & Daniel in the comments, it turned out I was:

  1. Impatient. Large chunks eventually show up with the pi function, it's just a bit slow on my simple old coding machine.
  2. Not handling buffering in any way.

So after rewriting the main function:

import System.IO

main :: IO ()
main =  do hSetBuffering stdout NoBuffering
           putStrLn pi'

It was fixed!

like image 93
okdewit Avatar answered Nov 15 '22 08:11

okdewit