GHC编译器不抱怨错误的代码路径

时间:2016-02-08 09:27:54

标签: haskell

我正在接近Haskell,认为将运行时错误转换为编译时错误。我希望编译器能够确定以下代码中的所有代码路径都没有错误。如果authorizeUser返回UnauthorizedCommand,那么对(command $ authorizeUser cmd userId)的调用将在运行时失败。

data Command = UnauthorizedCommand | Command {command :: String, userId :: String}

authorizedUsers = ["1", "2", "3"]

authorizeUser :: String -> String -> Command
authorizeUser cmd userId = if (userId `elem` authorizedUsers) then Command {command=cmd, userId=userId} else UnauthorizedCommand

main :: IO ()
main = do
  cmd <- getLine
  userId <- getLine
  case (command $ authorizeUser cmd userId) of
    "ls" -> putStrLn "works"
    _ -> putStrLn "not authorized"

是否有编译器开关来启用此类检查?

3 个答案:

答案 0 :(得分:5)

此示例中的主要问题是在具有多个构造函数的数据类型中使用记录语法最终可能会出现部分记录选择器函数。因此,除非所有构造函数具有相同的记录字段,否则不建议混合记录和多个构造函数。

最简单的解决方案是不在此处使用记录语法并定义您自己的<?php $user->roles()->detach(); ?> 访问器,它使错误案例显式为command返回类型:

Maybe

在编译期间检查此方法的一种稍微复杂的方法是使用GADT对类型中的权限级别进行编码。

data Command = UnauthorizedCommand | Command String String

command :: Command -> Maybe String
command UnauthorizedCommand = Nothing
command (Command cmd _)     = Just cmd

现在{-# LANGUAGE GADTs #-} {-# LANGUAGE DataKinds #-} data Permission = Authorized | Unauthorized data Command p where UnauthorizedCommand :: Command Unauthorized Command :: {command :: String, userId :: String} -> Command Authorized 选择器的类型为command,因此如果您尝试将其与未经授权的命令一起使用,则会出现编译时错误。

答案 1 :(得分:2)

你可以使用&#34; case&#34;仅在authorizeUser上,然后过滤DATA命令

data Command = UnauthorizedCommand | Command {command :: String, userId :: String}

authorizedUsers = ["1", "2", "3"]

authorizeUser :: String -> String -> Command
authorizeUser cmd userId = if (userId `elem` authorizedUsers) then Command {command=cmd, userId=userId} else UnauthorizedCommand

main :: IO ()
main = do
  cmd <- getLine
  userId <- getLine
  case  authorizeUser cmd userId of
    Command "ls" _ -> putStrLn "works"
    _ -> putStrLn "not authorized" -- either the user id is not good or the command is not good.

如果你有几个命令,你也可以这样做:

main = do
  cmd <- getLine
  userId <- getLine
  case  authorizeUser cmd userId of

    Command _ _ -> case cmd of
        "ls" -> putStrLn "works"
        "cd" -> putStrLn "moved!"
        _ -> putStrLn "Bad command!"
    _ -> putStrLn "not authorized" -- or: Unauthorized -> putStrLn "Not authorized"

PS:请注意,您可以考虑putStrLn s

答案 2 :(得分:1)

我想我终于理解了你的问题:你希望Haskell在任意command上调用Command时生成错误/警告。它与这个答案有关:https://stackoverflow.com/a/3804900/3421913。简而言之:

  1. 如果命令是Command Str StrUnauthorized,则无法在编译时检查(或非常非常困难):在您的情况下,它很简单,但可能更多复杂。

  2. 您有时会知道您的值具有良好的属性,但很难在haskell中表达它(请参阅上面的链接答案和duplicates/remove_duplicates函数)。在您的情况下,authorizeUser cmd userId不安全(因为cmduserId是用户输入),因此我们不会使用command函数,如果用户是猴。相反,我们将使用我在其他答案中提出的模式匹配。 如果您有一些不变量以确保c::Command不是Unauthorized,则可以直接使用command