我有以下代码:
import System.Environment
import System.Directory
import System.IO
import Data.List
dispatch :: [(String, [String] -> IO ())]
dispatch = [ ("add", add)
, ("view", view)
, ("remove", remove)
, ("bump", bump)
]
main = do
(command:args) <- getArgs
let result = lookup command dispatch
if result == Nothing then
errorExit
else do
let (Just action) = result
action args
errorExit :: IO ()
errorExit = do
putStrLn "Incorrect command"
add :: [String] -> IO ()
add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
view :: [String] -> IO ()
view [fileName] = do
contents <- readFile fileName
let todoTasks = lines contents
numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks
putStr $ unlines numberedTasks
remove :: [String] -> IO ()
remove [fileName, numberString] = do
handle <- openFile fileName ReadMode
(tempName, tempHandle) <- openTempFile "." "temp"
contents <- hGetContents handle
let number = read numberString
todoTasks = lines contents
newTodoItems = delete (todoTasks !! number) todoTasks
hPutStr tempHandle $ unlines newTodoItems
hClose handle
hClose tempHandle
removeFile fileName
renameFile tempName fileName
bump :: [String] -> IO ()
bump [fileName, numberString] = do
handle <- openFile fileName ReadMode
(tempName, tempHandle) <- openTempFile "." "temp"
contents <- hGetContents handle
let number = read numberString
todoTasks = lines contents
bumpedItem = todoTasks !! number
newTodoItems = [bumpedItem] ++ delete bumpedItem todoTasks
hPutStr tempHandle $ unlines newTodoItems
hClose handle
hClose tempHandle
removeFile fileName
renameFile tempName fileName
Run Code Online (Sandbox Code Playgroud)
试图编译它给我以下错误:
$ ghc --make todo
[1 of 1] Compiling Main ( todo.hs, todo.o )
todo.hs:16:15:
No instance for (Eq ([[Char]] -> IO ()))
arising from a use of `=='
Possible fix:
add an instance declaration for (Eq ([[Char]] -> IO ()))
In the expression: result == Nothing
In a stmt of a 'do' block:
if result == Nothing then
errorExit
else
do { let (Just action) = ...;
action args }
In the expression:
do { (command : args) <- getArgs;
let result = lookup command dispatch;
if result == Nothing then
errorExit
else
do { let ...;
.... } }
Run Code Online (Sandbox Code Playgroud)
我不知道为什么lookup回归Maybe a,我肯定可以比较Nothing.
(==)运营商的类型是Eq a => a -> a -> Bool.这意味着你只能比较对象是否相等,如果它们是一个实例的类型Eq.并且功能无法与平等相提并论:你会怎么写(==) :: (a -> b) -> (a -> b) -> Bool?没有办法做到这一点.1 虽然明确Nothing == Nothing和Just x /= Nothing,它的情况下Just x == Just y,当且仅当x == y; 因此,有没有办法写(==)的Maybe a,除非你可以写(==)的a.
这里最好的解决方案是使用模式匹配.一般来说,我发现自己if在Haskell代码中没有使用那么多语句.你可以写:
main = do (command:args) <- getArgs
case lookup command dispatch of
Just action -> action args
Nothing -> errorExit
Run Code Online (Sandbox Code Playgroud)
出于几个原因,这是更好的代码.首先,它更短,总是很好.其次,虽然你根本就不能(==)在这里使用,但假设dispatch相反持有列表.该case声明仍然同样有效(恒定时间),但比较Just x并Just y变得非常昂贵.其次,你不必重新绑定result使用let (Just action) = result; 这使得代码更短并且不会引入潜在的模式匹配失败(这很糟糕,尽管你知道它不能在这里失败).
1 ::事实上,(==)在保持参考透明度的同时,写不可能.在哈斯克尔,f = (\x -> x + x) :: Integer -> Integer并且g = (* 2) :: Integer -> Integer应该被认为是平等的,因为f x = g x所有人x :: Integer; 然而,证明两个函数以这种方式相等通常是不可判定的(因为它需要枚举无限数量的输入).而且你不能只说\x -> x + x等于语法相同的函数,因为那时你可以区分f,g即使它们做同样的事情.
该Maybe a类型只有一个Eq实例a- 如果有一个 - 这就是你得到的原因No instance for (Eq ([[Char]] -> IO ()))(一个函数不能与另一个函数比较).
也许可能的功能是你正在寻找的.我现在无法测试这个,但它应该是这样的:
maybe errorExit (\action -> action args) result
Run Code Online (Sandbox Code Playgroud)
也就是说,如果result是Nothing,则返回errorExit,但如果result是Just action,则应用lambda函数action.