0
votes

I'm trying to learn the idiomatic Haskell way to shuffle a list and take the first value off the head. I'm not even sure where in my program I should be doing this. So far, I've moved the logic to here:

-- {-# LANGUAGE UnicodeSyntax #-}
module RPS
    ( toss
    ) where

import System.Random.Shuffle
import Data.List

toss :: IO ()
toss =
    -- This doesn't actually work
    -- but I'd like to shuffle the list returned by
    -- getTosses and pull the first element out
    putStrLn "Biff tossed" ++ head shuffleM getTosses

getScissors :: [String]
getScissors =
    scissors where scissors = replicate 10 "✂️"

getPaper :: [String]
getPaper =
    paper where paper = replicate 10 "????"

getRocks :: [String]
getRocks =
    rocks where rocks = replicate 10 "????"

getPossibilities :: [String] -> [String] -> [String] -> [String]
getPossibilities rocks paper scissors =
    rocks ++ paper ++ scissors

getTosses :: [String]
getTosses =
    getPossibilities getRocks getPaper getScissors

While running this module in GHCi, I get

src/RPS.hs:11:36: error:
    • Couldn't match expected type ‘[[String] -> [a1]]’
                  with actual type ‘[a0] -> m0 [a0]’
    • Probable cause: ‘shuffleM’ is applied to too few arguments
      In the first argument of ‘head’, namely ‘shuffleM’
      In the second argument of ‘(++)’, namely ‘head shuffleM getTosses’
      In the expression:

The odd thing is, I can run shuffleM getTosses in GHCi just fine... it's only when I try to actually do that in a source file and then load that, that this fails. I've read that the shuffle monad only works in the context of IO, so I can't just shuffle a list in one of my other functions. I'm not sure if there's a better way.

1
First, putStrLn take only one argument. If you use ++, makes sure to warp the argument. So, write it as putStrLn (...) or putStrLn $ .... Second, just like putStrLn, head also take only one argument. - wisn
Don't forget that head :: [a] -> a. The String is a List of Char. If you take its head, it will be a Char. - wisn
putStrLn ("Biff tossed" ++ head (shuffleM getTosses)) does give me a type error like you mention. What is the proper way to print a Char? Do I need to use show? - Jim Wharton

1 Answers

1
votes

Try this.

toss :: IO ()
toss = do
    shuffled <- shuffleM getTosses
    putStrLn $ "Biff tossed" ++ head shuffled

The main problem is that shuffleM has kind of type MonadRandom m => [a] -> m [a]. As you can see, m is a MonadRandom. You can't pass a wrapped value as monad directly to a pure function such as head since head has kind of type [a] -> a. We need to unwrap the MonadRandom. One of many ways to solve this is using <- keyword.