2015-08-17 3 views
6

Я пытаюсь научиться Haskell, написав простую консольную шахматную игру. Он отображает шахматную доску и получает шаги со стандартного ввода в SAN notation. Вот что у меня до сих пор:Как обрабатывать ошибки времени выполнения в Haskell?

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 

Проблема заключается в том, что вызов функции moveSAN иногда происходит сбой программы, теряя весь прогресс, достигнутый в игре. Например, при нажатии Enter в "Ваш ход:" подсказка производит:

Your move: 
cchess: Prelude.head: empty list 

В противном случае, введя двузначное число производит:

Your move: 11 
cchess: Error in array index 

Ввод двух периодов дает:

Your move: .. 
cchess: Char.digitToInt: not a digit '.' 

Я хотел бы поймать эти ошибки и сообщить пользователю, что перемещение, которое они ввели, не соответствует действительной ноте SAN. Как я могу это сделать?

+0

Ваш код верен. Это ошибка в библиотеке chesshs, которую вы используете. При неправильном вводе он должен возвращать 'Left NoParse', но вместо этого он падает из-за использования частичных функций, таких как' head' и 'digitToInt' :( –

+0

Согласен, решение является полной функцией, которая может быть только сама из суммы функции. Вам нужно будет обернуть вызовы библиотеки в выписке, чтобы сделать общие функции ... –

+0

«Вам нужно будет обернуть вызовы библиотеки в выписке» Как это сделать? – hedgie

ответ

7

В идеальном мире вы должны избегать таких функций, как head и вообще не иметь ошибок во время выполнения. Ошибки Runtime принципиально неудобны, особенно в Haskell.

В этом случае вы хотите поймать ошибку времени выполнения. Если вы хотите превратить его в Maybe, вы можете использовать пакет spoon.

Исключения в Haskell являются тонкими благодаря лени. В частности, если вы не оцениваете ту часть структуры, которая имеет исключение, она не срабатывает. Это обрабатывается в spoon с двумя различными функциями:

  • spoon, которая оценивает структуру глубоко но требует экземпляр специального класса типов
  • teaspoon который только оценивает вашу структуру часть пути к weak head normal form

Для вашего дела, думаю, teaspoon должно быть хорошо. Попробуйте проверить результат в разборе с:

teaspoon (moveSAN move board) 

, который должен дать вам значение Maybe.

Если это не работает, это означает, что вам нужно оценить больше структуры, чтобы попасть в исключение. Похоже, Board не реализуют необходимые для класса типов глубоко оценить его spoon, поэтому лучше немного Hacky: использовать spoon на результате show:

spoon (show $ moveSAN move board) 

Это даст вам Maybe String. Если это Just, движение будет корректно проанализировано; если это Nothing, произошла ошибка.

Стоит отметить, что пакет spoon на самом деле не очень много: каждая функция у него есть только a couple of lines.В какой-то момент стоит выяснить, как обрабатывать исключения в Haskell самостоятельно, но на данный момент spoon немного более удобен и должен работать правильно для вас.

Смежные вопросы