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

Ric*_*ter 6 io haskell 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."
Run Code Online (Sandbox Code Playgroud)

Wil*_*sem 11

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

readRPS :: String -> Maybe RPS
readRPS "rock" = Just Rock
readRPS "paper" = Just Paper
readRPS "scissors" = Just Scissors
readRPS _ = Nothing
Run Code Online (Sandbox Code Playgroud)

因此,在这里我们返回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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,也许是完美的用例-我应该已经看到了。我只是注意到rps和rock需要切换才能使游戏正常运行。 (2认同)

Rob*_*ond 5

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

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

data RPS = Rock | Paper | Scissors deriving Read
Run Code Online (Sandbox Code Playgroud)

什么deriving Read确实是给RPS了默认实例的Read类型类,这在明显的方式工作:read "Rock"将成为Rock等等,所提供的编译器知道你正在使用read在类型的值的情况下RPS,预计。

然后,您需要在main函数中做的就是更改此代码:

putStrLn (_shoot choice Rock)
Run Code Online (Sandbox Code Playgroud)

putStrLn (_shoot (read choice) Rock)
Run Code Online (Sandbox Code Playgroud)

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

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

  • 如果您想提高安全性,还可以使用“ readMaybe”功能。 (3认同)