How to handle runtime errors in Haskell? - haskell

I'm trying to learn Haskell by writing a simple console chess game. It displays a chess board and gets moves from standard input in SAN notation. Here's what I've got so far:
import System.IO
import Chess
import Chess.FEN
main = do
putStrLn "Welcome to Console Chess!"
putStrLn $ show defaultBoard
gameLoop defaultBoard
gameLoop board = do
putStr "Your move: "
hFlush stdout
move <- getLine
let newBoard = moveSAN move board
case newBoard of
Left _ -> do
putStrLn "Invalid move, try again..."
gameLoop board
Right b -> do
putStrLn $ show b
gameLoop b
The problem is that the call to the moveSAN function sometimes crashes the program, losing all progress made in the game. For instance, pressing Enter at the "Your move:" prompt produces:
Your move:
cchess: Prelude.head: empty list
Otherwise, entering a two-digit number produces:
Your move: 11
cchess: Error in array index
Entering two periods gives:
Your move: ..
cchess: Char.digitToInt: not a digit '.'
I would like to catch these errors and inform the user that the move they have entered is not in valid SAN notation. How can I do that?

In an ideal world, you would avoid functions like head and not have runtime errors at all. Runtime errors are fundamentally awkward, especially in Haskell.
In this case, you want to catch the runtime error. If you're content to turn it into a Maybe, you can use the spoon package.
Exceptions in Haskell are subtle thanks to laziness. Particularly, if you don't evaluate the part of the structure that has the exception, it won't fire. This is handled in spoon with two different functions:
spoon, which evaluates a structure deeply but requires an instance of a special typeclass
teaspoon which only evaluates your structure part of the way to weak head normal form
For your case, I think teaspoon should be fine. Try checking the result of the parse with:
teaspoon (moveSAN move board)
which should give you a Maybe value.
If that doesn't work, it means you need to evaluate more of the structure to hit the exception. It looks like Board does not implement the typeclass needed to deeply evaluate it with spoon, so your best bet is a bit hacky: use spoon on the result of show:
spoon (show $ moveSAN move board)
This will give you a Maybe String. If it's Just, the move parsed correctly; if it's Nothing, there was an error.
It's worth noting that the spoon package doesn't really do much: each function it has is only a couple of lines. At some point, it's worth figuring out how to handle exceptions in Haskell yourself, but for now spoon is just a bit more convenient and should work properly for you.

Related

Can I have separate functions for reading and writing to a txt file in Haskell, without using a 'main' function?

I'm making a program using Haskell that requires simple save and load functions. When I call the save function, I need to put a string into a text file. When I call load, I need to pull the string out of the text file.
I'm aware of the complexities surrounding IO in Haskell. From some reading around online I have discovered that it is possible through a 'main' function. However, I seem to only be able to implement either save, or load... not both.
For example, I have the following function at the moment for reading from the file.
main = do
contents <- readFile "Test.txt"
putStrLn contents
How can I also implement a write function? Does it have to be within the same function? Or can I separate it? Also, is there a way of me being able to name the functions load/save? Having to call 'main' when I actually want to call 'load' or 'save' is rather annoying.
I can't find any examples online of someone implementing both, and any implementations I've found of either always go through a main function.
Any advice will be greatly appreciated.
I'm aware of the complexities surrounding IO in Haskell.
It's actually not that complex. It might seem a little intimidating at first but you'll quickly get the hang of it.
How can I also implement a write function?
The same way
Or can I separate it?
Yes
Also, is there a way of me being able to name the functions load/save?
Yes, for example you could do your loading like this:
load :: IO String
load = readFile "Test.txt"
All Haskell programs start inside main, but they don't have to stay there, so you can use it like this:
main :: IO ()
main = do
contents <- load -- notice we're using the thing we just defined above
putStrLn contents
Note the main is always what your program does; But your main doesn't only have to do a single thing. It could just as well do many things, including for instance reading a value and then deciding what to do; Here's a more complicated (complete) example - I expect you'll not understand all parts of it right off the bat, but it at least should give you something to play around with:
data Choice = Save | Load
pickSaveOrLoad :: IO Choice
pickSaveOrLoad = do
putStr "Do you want to save or load? "
answer <- getLine
case answer of
"save" -> return Save
"load" -> return Load
_ -> do
putStrLn "Invalid choice (must pick 'save' or 'load')"
pickSaveOrLoad
save :: IO ()
save = do
putStrLn "You picked save"
putStrLn "<put your saving stuff here>"
load :: IO ()
load = do
putStrLn "You picked load"
putStrLn "<put your loading stuff here>"
main :: IO ()
main = do
choice <- pickSaveOrLoad
case choice of
Save -> save
Load -> load
Of course it's a bit odd to want to do either save or load, most programs that can do these things want to do both, but I don't know what exactly you're going for so I kept it generic.

Haskell: Parse error: module header, import declaration or top-level declaration expected

I am saving some commands in a Haskell script in a .hs file while working thru a Haskell textbook. Here's a small example.
fst (1,2)
snd (1,2)
When I run these commands from the prelude in GHCi, they work fine. When I try to compile the .hs file with these two lines, I get the following:
ch4_test.hs:2:1: error:
Parse error: module header, import declaration
or top-level declaration expected.
|
2 | fst (1,2)
| ^^^^^^^^^
Failed, no modules loaded.
I've googled this error and can't find any explanation what I'm doing wrong.
From a newbie to future newbies: The interactive environment ghci would lead you to believe that you can punch some expressions into an .hs file and run the thing (in a similar fashion to languages like swift and ruby). This is not the case.
Haskell needs an entrypoint called main. Quoting:
Here is a simple program to read and then print a character:
main :: IO ()
main = do c <- getChar
putChar c
The use of the name main is important: main is defined to be the entry point of a Haskell program (similar to the main function in C), and must have an IO type, usually IO ()
Source: https://www.haskell.org/tutorial/io.html
You can't just put any expression in a hs file.
As the error message says, you need a declaration here. For example:
main =
print (fst (1,2)) >>
print (snd (1,2))
I am getting this error but the cause appears to be completely different from anything posted here. And the error message is not at all helpful.
Using Cabal version 3.6.2.0 with GHCI 8.10.7 on MacOS High Sierra (10.13)
I'm working from this page: https://www.tutorialspoint.com/haskell/haskell_modules.htm
specifically the "custom modules" section. There you can see the code I copied and pasted.
Besides the tutorial not mentioning I needed to add "other-modules: Custom" to myfirstapp.cabal, and besides the fact that the sample Custom.hs file includes "if x 'rem' 2 == 0" rather than "if x rem 2 == 0", here is the problem:
Indentation matters!
This line (inside the quotes) does NOT work "if x rem 2 == 0".
This line DOES work " if x rem 2 == 0"!
Indenting by one space is the difference between success and failure.
I'm totally new to Haskell. I've programmed extensively in PHP, Javascript, and Applescript, and dabbled in a dozen others, and this is the first time I've seen white space matter. I assume this is commonly known amongst Haskell veterans, but it would certainly be nice if that was included prominently in the documentation.

Haskell multiple actions within pattern match

For a game I want to use animations, for that I want to pick a picture from a list, and then go to the next picture. However, for that I would need to do two things within one function which is not possible I think.
getPicture :: [IO Picture] -> Int -> IO Picture
getPicture a i | i < length a = (!!) i a && getPicture a (i+1)
| otherwise = (!!) i a && getPicture a (0)
Obviously I cannot use && to proceed to the next part, but I was wondering if there was a possibility to do this?
Seeing as you're always recursively calling getPicture, it looks to me like you just want all the pictures in order. You also have a base case which goes back to the first picture. Don't you think getPictures :: [IO Picture] -> IO [Picture] looks like a more suitable signature? It would just get all pictures in a sequence, no need to manually keep track of i.
Luckily there's sequence from Control.Monad which can do exactly this for you. We'll throw in a cycle which accommodates the "go back to the beginning" part.
do
pictures <- cycle <$> sequence getPictures
… -- animate the pictures as you wish
Of course I'm just guessing what you might actually want. For example, this will keep all the pictures (frames?) loaded in memory.

Haskell: Conditionally execute external process with Maybe FilePath

I am struggling to understand a block of code which is extremely easy in imperative world.
That's what I need to do: given an executable full path, which is a Maybe FilePath type, I need to execute it conditionally.
If the path is a Nothing - print an error, if the path is Just Path - execute it and print message that the file has been executed. Only "Hello, World" can be easier,right?
But in Haskell I dug my self into numerous layers of Maybe's and IO's and got stuck.
Two concrete questions arise from here:
How do I feed a Maybe FilePath into a system or rawSystem? liftM does not work for me here.
What is the correct way of doing this kind of conditional branching?
Thanks.
Simple pattern matching will do the job nicely.
case command of
Just path -> system path >> putStrLn "Done"
Nothing -> putStrLn "None specified"
Or, if you'd rather not pattern-match, use the maybe function:
maybe (putStrLn "None specified") ((>> putStrLn "Done") . system) command
That may occasionally be nicer than matching with a case, but not here, I think. The composition with the printing of the success message is clunky. It fares better if you don't print messages but return the ExitCode in both branches:
maybe (return $ ExitFailure 1) system command
This is exactly what the Traversable type class was made for!
Prelude Data.Traversable System.Cmd> traverse system Nothing
Nothing
Prelude Data.Traversable System.Cmd> traverse system (Just "echo OMG BEES")
OMG BEES
Just ExitSuccess

Error with functions inside tuples in Haskell

i'm playing a little with Haskell and i'm stuck with this error, using the snd function with a tuple (String, list).
snd ("Felix Felices",[("Escarabajos Machacados",52,[f1,f2]),("Ojo de Tigre Sucio",2,[f2])])
ERROR - Cannot find "show" function for:
*** Expression : snd ("Felix Felices",[("Escarabajos Machacados",52,[f1,f2]),("Ojo de Tigre Sucio",2,[f2])])
*** Of type : [([Char],Integer,[(Integer,Integer,Integer) -> (Integer,Integer,Integer)])]
The thing is, if i remove f1, f2 and f3 (they are functions) the code works just fine, it returns the list. Why is this happening, why can't i just put a function inside the tuple's second position?
You can put functions into tuples. But you can't display them - ghci wants to show the result and print it. How should it show (convert to string) the functions? It can't, or at least nobody felt like choosing one way of doing it (which would propably be flawed anyway - at least I can't think of any approach that doesn't have holes to huge even I can see them). Therefore, you can't evaluate something that returns functions or collections of functions in ghci.
The problem isn't really the functions f1 f2 or f3, the problem is that you are trying to print these functions, but functions don't have an instance of Show, so they cannot be printed. However if you try:
Prelude> snd ("Felix Felices",[("Escarabajos Machacados",52,["f1","f2"]),("Ojo de Tigre Sucio",2,["f2"])])
you get the result:
[("Escarabajos Machacados",52,["f1","f2"]),("Ojo de Tigre Sucio",2,["f2"])]
So the problem isn't that you cannot have a function in a tuple, the problem is that you cannot convert functions to strings so they can be printed.
module Text.Show.Functions provides an instance Show (a -> b).
ghci> :m +Text.Show.Functions
ghci> [(*), (/)]
[<function>,<function>]
It's not useful for actually figuring out what the functions are, but there's no good way to do that anyway (well, the debugger and vacuum aside). But if you just want some Show instances for convenience, this is in the standard library.
You cannot print bare functions in Haskell as there is no "show" function defined for them.
You get the same type of error, if you type
Hugs> sqrt
for example
The system tells the type of the expression, which in your case is ([Char],Integer,[(Integer,Integer,Integer) -> (Integer,Integer,Integer)])], but cannot print it, because it is a function.

Resources