Haskell:如何检查IO上的运行时类型?

时间:2019-06-09 10:13:29

标签: haskell io typechecking

我正在逐步介绍Haskell上的一些入门资料,并尝试为命令行完成此愚蠢的Rock,Paper和Scissors实现。

我认为输入的类型防护足以使编译器确信输入为RPS类型,但可惜不是。

如何告诉编译器输入数据是一种类型还是另一种类型?


data RPS = Rock | Paper | Scissors

_shoot :: RPS -> RPS -> String
_shoot Rock Paper = "Paper beats rock, you win!"
_shoot Paper Rock = "Paper beats rock, you loose."
_shoot Rock Scissors = "Rock beats scissors, you loose."
_shoot Scissors Rock = "Rock beats scissors, you win!"
_shoot Paper Scissors = "Scissors beats paper, you win!"
_shoot Scissors Paper = "Scissors beats paper, you loose!"
_shoot Rock Rock = "Tie!"
_shoot Scissors Scissors = "Tie!"
_shoot Paper Paper = "Tie!"

isRPS :: String -> Bool
isRPS s = elem s ["Rock", "Paper", "Scissors"]

main :: IO ()
main = do
  putStrLn "Rock, Paper, or Scissors?"
  choice <- getLine
  if isRPS choice -- this was my idea but is apparently not good enough
    then putStrLn (_shoot choice Rock) 
--                        ^^^^^^
-- Couldn't match type ‘[Char]’ with ‘RPS’ Expected type: RPS Actual type: String
    else putStrLn "Invalid choice."

2 个答案:

答案 0 :(得分:11)

您没有将choice(是String)转换为RPS,甚至没有将Maybe RPS转换为:

readRPS :: String -> Maybe RPS
readRPS "rock" = Just Rock
readRPS "paper" = Just Paper
readRPS "scissors" = Just Scissors
readRPS _ = Nothing

因此,在给定输入有效的情况下,我们在此返回Just x(与x对应的RPS项),或者如果字符串不是有效的选项,则返回Nothing

然后我们可以将其实现为:

import Data.Char(toLower)

main :: IO ()
main = do
    putStrLn "Rock, Paper, or Scissors?"
    choice <- getLine
    case readRPS (map toLower choice) of
        Just rps -> putStrLn (_shoot rps Rock) 
        Nothing -> putStrLn "Invalid choice."
    main

答案 1 :(得分:5)

您快到了,只需使用read函数即可将用户的字符串转换为RPS数据类型。

您需要做的第一件事是使RPSRead类型类的实例。通过将data声明修改为:

,可以轻松完成此操作。
data RPS = Rock | Paper | Scissors deriving Read

deriving Read所做的就是为RPS提供Read类型类的默认实例,该实例的工作方式很明显:read "Rock"将变成Rock,依此类推,前提是编译器知道您正在使用read类型值的上下文中使用RPS

然后,您需要在main函数中进行的所有更改:

putStrLn (_shoot choice Rock)

putStrLn (_shoot (read choice) Rock)

由于_shoot的类型签名告诉GHC,其第一个参数必须为RPS值,因此它将知道使用为read定义的RPS实例类型,并且一切都应该很好,因为您已经将有效的用户选择限制为这3个特定的字符串。

(请注意,对于较大的程序,有更安全,更好的方式来处理此类事情-请参阅Willem的答案中的一种简单方法-但这对于基本的学习练习来说是很好的。)