0

I am new to monads in Haskell.

I was trying to write a scatter graph with Haskell-chart that would plot 20 points with strong (but not perfect) positive correlation. Example of what I was trying to do. Specifically, I want a list of random doubles in the range 0.8 to 1.0 to multiply with my y-vals to reduce the correlation coefficient a bit. My original implementation was this:

import Graphics.Rendering.Chart.Easy
import Graphics.Rendering.Chart.Backend.Cairo
import System.Random
import Lib (xvals)

main = toFile def "chart.png" $ do
  layout_title .= "My chart"
  layout_x_axis . laxis_override .= axisGridHide
  plot (points "My points" [(x,y) | (x,y) <- let vs = xvals 20 0.0 10.0 in zip vs $ zipWith (*) vs noise])
     where noise = filter (>0.8) $ randoms $ mkStdGen 1

which worked, but had the flaw that since the seed for mkStdGen was static, I would get the same points regardless of how many times I rebuilt and executed the code. So I tried this to generate a dynamic seed:

import Graphics.Rendering.Chart.Easy
import Graphics.Rendering.Chart.Backend.Cairo
import System.Random
import Lib (xvals)

main = toFile def "chart.png" $ do
  layout_title .= "My chart"
  layout_x_axis . laxis_override .= axisGridHide
  seed <- (randomIO :: IO Int)
  plot (points "My points" [(x,y) | (x,y) <- let vs = xvals 20 0.0 10.0 in zip vs $ zipWith (*) vs noise)])
     where noise = filter (>0.8) $ randoms $ mkStdGen seed

The error message I get for the above is:

• Couldn't match type ‘IO’
                 with ‘transformers-0.5.6.2:Control.Monad.Trans.State.Lazy.StateT
                         (Layout Double Double)
                         (transformers-0.5.6.2:Control.Monad.Trans.State.Lazy.State CState)’
  Expected type: transformers-0.5.6.2:Control.Monad.Trans.State.Lazy.StateT
                   (Layout Double Double)
                   (transformers-0.5.6.2:Control.Monad.Trans.State.Lazy.State CState)
                   Int
    Actual type: IO Int

I think it is due to toFile def "chart.png" but I don't know why, or how to get this working with randomIO. As far as I know, seed :: Int so this should work. Any help would be appreciated.

m1531
  • 11
  • 2
  • The `toFile` function probably returns `IO ()`, but its 3rd argument is **not** an IO action. So your single `do` construct does **not** live in the `IO` monad. This is what the error message means. Instead, you could start normally with `main = do` ; so you start inside `IO`. In your main do loop, you can do two things in sequence: 1) extract the seed from IO , and then 2) run the `toFile` function. - Note that an alternative consists in passing the seed as a command line parameter. That way, you can decide whether you get the same random sequence as before or not. – jpmarinier Mar 05 '22 at 19:52

1 Answers1

1

If anyone is interested, I got this working with MonadRandom and @jpmarinier's suggestion and @chi's suggestion to use lets instead of wheres:

import Graphics.Rendering.Chart.Easy
import Graphics.Rendering.Chart.Backend.Cairo
import Control.Monad.Random.Class
import Lib (xvals)

main = do
  rands <- (getRandoms :: MonadRandom m => m [Double])
  toFile def "chart.png" $ do
  layout_title .= "My chart"
  layout_x_axis . laxis_override .= axisGridHide
  let noise = filter (>0.8) rands
  let xs = xvals 20 0.0 10.0
  let ys = zipWith (*) xs noise
  plot (points "My points" [(x,y) | (x,y) <- zip xs ys])

This way, we don't really need to faff with RandomGens. I dunno if this is the most ideal solution, so cause I am a bit of a noob at this, please let me know if there is a better way of doing this.

m1531
  • 11
  • 2