I have the following code:
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
Trying to compile it gives me the following error:
$ 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 ...;
.... } }
I don’t get why is that since lookup returns Maybe a, which I’m surely can compare to Nothing.
The type of the
(==)operator isEq a => a -> a -> Bool. What this means is that you can only compare objects for equality if they’re of a type which is an instance ofEq. And functions aren’t comparable for equality: how would you write(==) :: (a -> b) -> (a -> b) -> Bool? There’s no way to do it.1 And while clearlyNothing == NothingandJust x /= Nothing, it’s the case thatJust x == Just yif and only ifx == y; thus, there’s no way to write(==)forMaybe aunless you can write(==)fora.There best solution here is to use pattern matching. In general, I don’t find myself using that many
ifstatements in my Haskell code. You can instead write:This is better code for a couple of reasons. First, it’s shorter, which is always nice. Second, while you simply can’t use
(==)here, suppose thatdispatchinstead held lists. Thecasestatement remains just as efficient (constant time), but comparingJust xandJust ybecomes very expensive. Second, you don’t have to rebindresultwithlet (Just action) = result; this makes the code shorter and doesn’t introduce a potential pattern-match failure (which is bad, although you do know it can’t fail here).1:: In fact, it’s impossible to write
(==)while preserving referential transparency. In Haskell,f = (\x -> x + x) :: Integer -> Integerandg = (* 2) :: Integer -> Integerought to be considered equal becausef x = g xfor allx :: Integer; however, proving that two functions are equal in this way is in general undecidable (since it requires enumerating an infinite number of inputs). And you can’t just say that\x -> x + xonly equals syntactically identical functions, because then you could distinguishfandgeven though they do the same thing.