2

次のコードがあります。

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ます。

4

2 に答える 2

9

(==)演算子のタイプは ですEq a => a -> a -> Bool。これが意味することは、オブジェクトが のインスタンスであるタイプである場合にのみ、オブジェクトが等しいかどうかを比較できるということですEq。そして、関数は同等性を比較することはできません: どのように書き(==) :: (a -> b) -> (a -> b) -> Boolますか? それを行う方法はありません。1 そして、明らかにNothing == NothingandJust x /= Nothingである一方で、 Just x == Just yif and only if x == y;の場合です。したがって、 for を書くことができない限り、 for を書く方法はありませ(==)ん。Maybe a(==)a

ここでの最善の解決策は、パターン マッチングを使用することです。if一般に、Haskell コードでこれほど多くのステートメントを使用することはありません。代わりに次のように書くことができます:

main = do (command:args) <- getArgs
          case lookup command dispatch of
            Just action -> action args
            Nothing     -> errorExit

これは、いくつかの理由でより優れたコードです。まず、それはより短いです。これは常に素晴らしいことです。第 2 に、ここでは使用できませんが、代わりにリストを保持しているとします。ステートメントは同じように効率的 (一定時間) のままですが、比較すると非常にコストがかかります。次に、 ;で再バインドする必要はありません。これにより、コードが短くなり、潜在的なパターン マッチ エラーが発生しなくなります (これは悪いことですが、ここで失敗することはありません)。(==)dispatchcaseJust xJust yresultlet (Just action) = result


1:(==) : 実際、参照透過性を維持しながら書くことは不可能です。Haskell ではf = (\x -> x + x) :: Integer -> Integer、for all ;g = (* 2) :: Integer -> Integerは等しいと見なされるべきです。ただし、この方法で 2 つの関数が等しいことを証明することは、一般に判断できません (無限の数の入力を列挙する必要があるため)。そして、構文的に同一の関数に等しいとだけ言うことはできません。なぜなら、同じことをしているにもかかわらず、区別できるからです。f x = g xx :: Integer\x -> x + xfg

于 2012-07-31T17:48:45.830 に答える
8

Maybe a型にはインスタンスがある場合にEqのみインスタンスがありますa-それが取得する理由ですNo instance for (Eq ([[Char]] -> IO ()))(関数を別の関数と比較することはできません)。

多分関数はあなたが探しているものです。現時点ではこれをテストできませんが、次のようになるはずです。

maybe errorExit (\action -> action args) result

つまり、 が の場合resultNothingを返しますerrorExitが、 が の場合resultJust actionにラムダ関数を適用しますaction

于 2012-07-31T17:38:25.777 に答える