Why is gnuplot plot not receiving the entirety of stdin from getContents?

138 Views Asked by At

I'm having an issue with lazy IO, but I don't know how to fix it.
I've got three small test programs here, but with V2 being the thing I actually want.
Somewhere, it seems that either getContents is being halted early, or gnuplot is finishing writing early.

The crux of the question is 'how can I take stuff from stdin, and plot it with gnuplot here', but I'd also like to know how to debug the underlying problem.

Version 1, no dealing with gnuplot. Run with paste <(seq 10000) <(seq 10000) | runhaskell /tmp/hasktest2.hs, prints out (10000.0,10000.0) as expected. Clearly all of stdin is loaded.

import Data.List
main = do
  contents <- getContents
  print . last . map f . lines $ contents

f :: String -> (Double, Double)
f s = (read x, read y)
  where
    [x,y] = words s

V2: Attempting to plot whatever comes from stdin. This is run the same way as V1 - the temporary file that gnuplot makes gets truncated, so I don't get a plot. However, if I run with with 1000 instead of 10k, it does work - it gets truncated at some point when writing the gnuplot csv file, so I have a line that looks like 1767.0, 1767 with no \n.

main = do
  contents <- getContents
  plotPathStyle [] (PlotStyle Points (DefaultStyle (1))) . map f . lines $ contents

f :: String -> (Double, Double)
f s = (read x, read y)
  where
    [x,y] = words s

V3: Just to test that gnuplot can actually deal with 10k points, and write them to a file - this produces a plot, as expected.

import Graphics.Gnuplot.Simple

main = plotPathStyle [] (PlotStyle Points (DefaultStyle (1))) (zip [1..10000] [1..10000] :: [(Double, Double)])
1

There are 1 best solutions below

0
On BEST ANSWER

It's very much depending on a race condition what you'll end up getting, and whether you get a plot at all.

The function plotPathStyle forks a new Haskell thread in which gnuplot is called. This thread makes use of the list you pass, so if the list is obtained via lazy IO, only this thread will actually read the file. The function plotPathStyle returns more or less immediately, and at the end of the main thread, the program will shut down.

So depending on how scheduling occurs, you may see truncated output or no gnuplot window at all. (If I actually compile the program rather than invoke via runhaskell, I usually get no plot whatsoever.) Even forcing the list won't save you from this condition. If you want non-interactive use (i.e., not from within GHCi), it seems that the gnuplot package recommends the interface in Graphics.Gnuplot.Advanced, which gives you more control and e.g. allows you to wait explicitly for the plot to finish.