我正在接近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"
是否有编译器开关来启用此类检查?
答案 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。简而言之:
如果命令是Command Str Str
或Unauthorized
,则无法在编译时检查(或非常非常困难):在您的情况下,它很简单,但可能更多复杂。
您有时会知道您的值具有良好的属性,但很难在haskell中表达它(请参阅上面的链接答案和duplicates/remove_duplicates
函数)。在您的情况下,authorizeUser cmd userId
不安全(因为cmd
和userId
是用户输入),因此我们不会使用command
函数,如果用户是猴。相反,我们将使用我在其他答案中提出的模式匹配。
如果您有一些不变量以确保c::Command
不是Unauthorized
,则可以直接使用command
。