-2

I have the following function:

getBoard :: String
getBoard = do
    contents <- readFile "src/board.txt"
    return contents

and I'm getting this error:

• Couldn't match type ‘IO’ with ‘[]’ Expected type: [String] Actual type: IO String

Already read a lot of things on the internet, and apparently my code should work. Can anyone tell me what's wrong?

Obs: When I go to repl and do:

contents <- readFile "src/board.txt"

and then:

:type contents

It says to me that contents is a String. So, I can't figure out why I'm getting the error I put above.

Note: The getBoard function MUST return a String

  • 1
    You could also ask the repl for the type of `return`. In Haskell, `return` is just a function. Its type is: `return :: Monad m => a -> m a`. Now the question is: which monad did you want or expect ? – jpmarinier Aug 15 '21 at 23:53
  • What you are saying is that I need to have another function of type IO String -> String ? – Thalles Portilho Aug 16 '21 at 00:06
  • 1
    @ThallesPortilho the type system will not let you do arbitrary side effects in a function: "The getBoard function MUST return a String" it literally *can't*. Or to put it differently, if you want a function to return a plain string it can't read it from a file (or STDIN, or...). – Jared Smith Aug 16 '21 at 00:09
  • 1
    @ThallesPortilho Well, a polymorphic function of type `IO a -> a` exists in the library. Hoogle will happily point you to it, this is [`unsafePerformIO`](https://hackage.haskell.org/package/base-4.15.0.0/docs/System-IO-Unsafe.html#v:unsafePerformIO). But the clue is in the name. If you use for example `getLine` thru `unsafePerformIO`, you will always read the same line, because of *referential transparency*. Details in the [older question](https://stackoverflow.com/questions/11467066/how-to-get-normal-value-from-io-action-in-haskell) pointed to by the person who closed your question – jpmarinier Aug 16 '21 at 12:06
  • Related: https://stackoverflow.com/q/4056867 . – atravers Sep 26 '21 at 08:54

1 Answers1

2

You've written:

getBoard :: String

That doesn't say that getBoard is a function or procedure that returns a String. Rather, it says that getBoard is a String; I mean some specific String like "Hello world" or "foo", for example. Obviously, that's wrong. Rather, getBoard is a procedure you can perform that produces a String as its result. In Haskell, the way you say that is:

getBoard :: IO String

So that's the right answer. If that answer doesn't work for you, please provide more context so we can understand why.

When you say "Note: The getBoard function MUST return a String", that's almost exactly what getBoard :: IO String means: getBoard is a procedure that returns a String. (One subtle point here is that I've said "procedure" instead of "function". That's because in Haskell, "function" means something different from what you're probably expecting from other languages. A function is something that takes parameters. Because getBoard doesn't take parameters, it's not a function, but it is a procedure.)

Did you mean that you want it to be a String, rather than return one? This doesn't make much sense, though. Which specific String would you like it to be?


Okay, now is where I reluctantly admit that there is one way to tell Haskell (incorrectly) that getBoard is a String. You shouldn't do this unless you're a pretty advanced Haskell programmer, understand the implications, and have a good reason it's worth it. But you can import System.IO.Unsafe and then wrap the definition of getBounds with the unsafePerformIO function. What this says is: "Pretend that getBounds is some specific String rather than a procedure. Any time you need to know which String it is, go sneak off behind the scenes and run this procedure to find out."

Note that if you do this, there is NO guarantee about when your program will read the file, how many times your program will read the file, or even whether your program will read the file at all! All of these things can depend on things like strictness of evaluation, which optimization choices like inlining the compiler makes, and so on. If the contents of the file change while your program is running, the result is undefined. This is not a normal way to write Haskell, other Haskell programmers will give you weird looks and assume you're kind of clueless if you do it without a good reason, and you should probably just forget about it until you're more on the advanced side. I only mentioned it because I don't want to give an incomplete response.

Chris Smith
  • 2,615
  • 19
  • 18