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

Mic*_*kov 2 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
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.

Ant*_*sky 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
Run Code Online (Sandbox Code Playgroud)

出于几个原因,这是更好的代码.首先,它更短,总是很好.其次,虽然你根本就不能(==)在这里使用,但假设dispatch相反持有列表.该case声明仍然同样有效(恒定时间),但比较Just xJust 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 - > Maybe a - > Bool`,你_can_实现`(== Nothing)::也许a - > Bool`; 这个函数的名称是[isNothing](http://www.haskell.org/hoogle/?hoogle=Maybe+a+-%3E+Bool). (4认同)

leg*_*cia 8

Maybe a类型只有一个Eq实例a- 如果有一个 - 这就是你得到的原因No instance for (Eq ([[Char]] -> IO ()))(一个函数不能与另一个函数比较).

也许可能的功能是你正在寻找的.我现在无法测试这个,但它应该是这样的:

maybe errorExit (\action -> action args) result
Run Code Online (Sandbox Code Playgroud)

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

  • 如果你不想重构你的代码,你也应该能够用`isNothing results'替换`result == Nothing` (4认同)
  • 请注意,`(\ action - > action args)`也可以写成`($ args)`. (2认同)