我有以下代码:
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
尝试编译它会给我以下错误:
$ 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 ...;
.... } }
我不明白为什么会因为lookup
返回Maybe a
,我肯定可以与Nothing
进行比较。
答案 0 :(得分:9)
(==)
运算符的类型为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
语句。你可以写:
(==)
由于几个原因,这是更好的代码。首先,它更短,总是很好。其次,虽然你只是不能在这里使用dispatch
,但假设case
代替了列表。 Just x
语句仍然有效(恒定时间),但比较Just y
和result
变得非常昂贵。其次,您不必使用let (Just action) = result
重新绑定(==)
;这使得代码更短,并且不会引入潜在的模式匹配失败(这很糟糕,尽管你知道它不能在这里失败)。
1::实际上,在保留引用透明度的同时编写f = (\x -> x + x) :: Integer -> Integer
是不可能的。在Haskell中,g = (* 2) :: Integer -> Integer
和f x = g x
应该被认为是平等的,因为x :: Integer
适用于所有\x -> x + x
;然而,证明两个函数以这种方式相等通常是不可判定的(因为它需要枚举无限数量的输入)。而且你不能只说f
只等于语法相同的函数,因为那样你就可以区分g
和{{1}},即使它们做同样的事情。
答案 1 :(得分:8)
Maybe a
类型只有Eq
有一个a
实例 - 这就是为什么你得到No instance for (Eq ([[Char]] -> IO ()))
(一个函数无法与另一个函数进行比较)。
也许maybe function正是您所寻找的。我现在无法测试这个,但它应该是这样的:
maybe errorExit (\action -> action args) result
也就是说,如果result
为Nothing
,则返回errorExit
,但如果result
为Just action
,请在action
上应用lambda函数