为什么我不能在Haskell中将查找结果与Nothing进行比较?

时间:2012-07-31 17:31:07

标签: haskell

我有以下代码:

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进行比较。

2 个答案:

答案 0 :(得分:9)

(==)运算符的类型为Eq a => a -> a -> Bool。这意味着,如果对象属于Eq的实例,则只能比较对象的相等性。并且函数不具有可比性:你会如何写(==) :: (a -> b) -> (a -> b) -> Bool?没有办法做到这一点。 1 虽然显然是Nothing == NothingJust 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 yresult变得非常昂贵。其次,您不必使用let (Just action) = result重新绑定(==);这使得代码更短,并且不会引入潜在的模式匹配失败(这很糟糕,尽管你知道它不能在这里失败)。


1::实际上,在保留引用透明度的同时编写f = (\x -> x + x) :: Integer -> Integer是不可能的。在Haskell中,g = (* 2) :: Integer -> Integerf 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

也就是说,如果resultNothing,则返回errorExit,但如果resultJust action,请在action上应用lambda函数