0

I need to convert a list of IO [Float] to [Float]. I get a IO [Float] object from the following function:

probs :: Int -> IO [Float]
probs 0 = return []
probs n = do
             p <- getStdRandom random
             ps <- probs (n-1) 
             return (p:ps)

I understand that the result of this function is a list with type [IO Float] and not a list of numbers. It is a list of I/O actions that generate numbers. The I/O wasn't executed yet, so in my case the random number generator hasn't actually generated the numbers. What I want to do is generate each random number of the contents of this list, so I can get a [Float] list.

I need this to calculate the amount of numbers in every quartile of the result (to check the distribution among random numbers):

calcQuartile :: [Float] -> [Float] -> [Int]
calcQuartile randomList (x1:x2:rest) = length(filter (\x -> x>=x1 && x<x2) randomList):calcQuartile randomList (x2:rest)
calcQuartile x y = []

I use the following code to run this function, which is not working:

calcQuartile (probs x) [0,0.25..1]

The error which I get is:

 • Couldn't match expected type ‘[Float]’
               with actual type ‘IO [Float]’
 • In the first argument of ‘calcQuartile’, namely ‘(probs x)’
   In the expression: calcQuartile (probs x) [0, 0.25 .. 1]
   In an equation for ‘getAmountInQuartile’:
       getAmountInQuartile x = calcQuartile (probs x) [0, 0.25 .. 1]
Simon Baars
  • 1,877
  • 21
  • 38
  • 3
    The idea is that one does *not* unwrap the values of an `IO`. There are some `unsafePerformIO` and friends functions, but this can result in severe problems. One writes pure functions, and you can for example then bind two functions together with for example `probs x >>= \y -> return (calcQuartile y [0, 0.25..1])` – Willem Van Onsem Sep 13 '18 at 09:14
  • 1
    In practice, you would just write ``(`calcQuartile`[0, 1/4 .. 1]) <$> probs x``. (The purpose of the backticks is to partially apply to the right argument. It would be easier if you had defined `calcQuartile` with flipped arguments in the first place, that would simplify the application to `calcQuartile [0, 1/4 .. 1] <$> probs x`.) Alternatively: `calcQuartile <$> probs x <*> pure [0, 1/4 .. 1]`. – leftaroundabout Sep 13 '18 at 09:36
  • Incidentally, for more involved stuff with random numbers there are dedicated monads, so you can avoid doing everything in `IO` in the first place. http://hackage.haskell.org/package/random-fu-0.2.7.0/docs/Data-Random.html – leftaroundabout Sep 13 '18 at 09:44
  • @leftaroundabout I must say I don't find my question a duplicate from the linked questions as my question explicitly states that it is about an IO **list** and that it deals with **random** values. I had hoped for some solution that deals with monad lists in specific (ways to `fold` over a monadic list (`foldM` perhaps?) or `map` or `filter` or whatever). That the answer to this question is now similar to the answers of the other question, doesn't mean that I expected to find similar answers. – Simon Baars Sep 13 '18 at 19:04

1 Answers1

-1

Edit: As @freestyle style noted, this solution works but can lead to unpredicible results:

I imported the module System.IO.Unsafe:

import System.IO.Unsafe

Next, I can replace my code by the following:

calcQuartile (unsafePerformIO(probs x)) [0,0.25..1]

The better solution, as noted by @Willem Van Onsem is the following:

probs x >>= \y -> return (calcQuartile y [0, 0.25..1])

It is noteable though that this returns a IO [Int] instead of a [Int]. The monadic bind >>= links two impure actions together (i.e. in this case IO actions) and makes a single IO action out of them. The same thing can also be written with do notation:

do
  y <- probs x
  return $ calcQuartile y [0, 0.25 .. 1]

In this particular case, the second action (the calcQuartile) is actually pure (thus the return, which wraps a pure value as a side-effect-free IO action), so you don't really need the monad instance at all and can instead just use IO as a functor:

fmap (\y -> calcQuartile y [0, 0.25..1]) $ probs x

or

(`calcQuartile`[0, 0.25..1]) <$> probs x
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
Simon Baars
  • 1,877
  • 21
  • 38
  • 2
    Yes, it works, but how does it work? Can you predict and explanation result: `let {a = calcQuartile (unsafePerformIO(probs x)) [0,0.25..1]; b = calcQuartile (unsafePerformIO(probs x)) [0,0.25..1]} in a == b` – freestyle Sep 13 '18 at 09:10
  • @freestyle But isn't that the whole thing when working with random numbers? Random results are not entirely predictable by nature. How would you suggest this usecase could be solved in a more "clean" manner? – Simon Baars Sep 13 '18 at 09:16
  • @SimonBaars If you enable optimizations, freestyle's code will suddenly return `True`, not `False`. – sepp2k Sep 13 '18 at 09:20
  • 4
    This is emphatically **not** a safe use of `unsafePerformIO`. As freestyle says, it breaks referential transparency: if you want to check multiple different samples from the same distribution, you'd expect to get always slightly different results, but `unsafePerformIO (probs x)` is semantically just a constant. As a rule of thumb, never use `unsafePerformIO`, ever. (The few cases where it is actually appropriate, you must be 100% sure and wouldn't have to ask a question about it.) _Definitely_ don't use it if you're not really firm with monads yet. – leftaroundabout Sep 13 '18 at 09:29
  • 1
    To everybody: though I downvoted this answer myself, there's no need to overdo it. This is a perfectly well-intended post, no need to punish anything. It just happens to contain a solution that should be avoided. – leftaroundabout Sep 13 '18 at 11:28
  • @leftaroundabout Sorry... I did not know about the unpredictible results. I edited my answer so people who discover this question will not be lead towards bad practise solutions. – Simon Baars Sep 13 '18 at 12:38
  • @leftaroundabout Thanks a lot for elaborating upon my answer :-). – Simon Baars Sep 13 '18 at 19:06