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

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

最佳答案

(==) 的类型运算符是 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相反,持有 list 。 case语句仍然有效(恒定时间),但比较 Just xJust y变得非常昂贵。其次,您不必重新绑定(bind)resultlet (Just action) = result ;这使得代码更短,并且不会引入潜在的模式匹配失败(这很糟糕,尽管您知道它不会在这里失败)。

<小时/>

1::事实上,不可能写(==)同时保持引用透明度。在 haskell ,f = (\x -> x + x) :: Integer -> Integerg = (* 2) :: Integer -> Integer应该被认为是相等的,因为 f x = g x对于所有人x :: Integer ;然而,以这种方式证明两个函数相等通常是不可判定的(因为它需要枚举无限数量的输入)。你不能只是说\x -> x + x只等于语法相同的函数,因为这样你就可以区分 fg即使他们做同样的事情。

关于haskell - 为什么我不能将查找结果与 Haskell 中的 Nothing 进行比较?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11745799/

相关文章:

haskell - 使用 Parsec 按字符串正确分割

haskell - 从 ErrorT 转换到 exceptT

haskell - 相等的模式匹配

haskell - 如何使用 Select monad 解决 n-queens?

haskell - 使用 Data.Machine,如何将两个 ProcessT 组合在一起来修改两个不同的状态?

haskell - 如何防止抢劫模板中的 JavaScript 逃逸?

haskell - 如何在功能上生成广度优先的树。 (与 haskell )

haskell - 'readFile'的异常处理

haskell - 在 `unsafeDupablePerformIO` 内分配内存是否安全?

haskell - 使用 GHC.Generics 限制泛型函数域