webエンジニアの日常

RubyやPython, JSなど、IT関連の記事を書いています

すごいH本のToDoリストのコードを修正する

すごいHaskell楽しく学ぼうという本でHaskellを勉強しているのですが、ToDoリストをもっと楽しむという章に書いてあるコードをそのまま写経して実行してもエラーが出る(コンパイルエラーなので実行もできない)ので、正しいコードを載せておきます。

import System.Environment
import System.Directory
import System.IO
import Data.List
import Control.Exception

dispatch :: String -> [String] -> IO()
dispatch "add" = add
dispatch "view" = view
dispatch "remove" = remove

main = do
  (command:argList) <- getArgs
  dispatch command argList

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
  contents <- readFile fileName
  let todoTasks = lines contents
      number = read numberString
      newTodoItems = unlines $ delete (todoTasks !! number) todoTasks
  bracketOnError (openTempFile "." "temp")
    (\(tempName, tempHandle) -> do
      hClose tempHandle
      removeFile tempName
    )
    (\(tempName, tempHandle) -> do
      hPutStr tempHandle newTodoItems
      hClose tempHandle
      removeFile fileName
      renameFile tempName fileName
    )

書籍との差分

+ import Control.Exception

まず、bracketsOnErrorを使うのでControl.Exceptionモジュールが必要になります。

- numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks

numberedTasksはタスクの一覧を表示するために使うが、これはviewという関数で実装したので、removeには必要ありません。

実行例をみても、removeには何も表示されない

なので、remove関数内の表示系は全て削除します。

- putStrLn "There are your ToDo items:"
- mapM_ putStrLn numberedTasks

以上です。

すごいH本のToDoリストを写経して動かない!となった方の参考になれば幸いです。

といっても、ここまで丁寧に読んでいれば分かることだと思います。

ちなみに、すごいH本は本当にいい入門書なので、これからHaskellを勉強したいという人にはぴったりです

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

おまけ

練習課題として、受け取った番号のToDoを先頭に持ってくるbumpコマンドの実装例も載せておきます。

中身はほとんどremoveと同じです

bump :: [String] -> IO()
bump [fileName, numberString] = do
  contents <- readFile fileName
  let todoTasks = lines contents
      number = read numberString
      bumpItem = todoTasks !! number
      newTodoItems = (unlines $ bumpItem : delete bumpItem todoTasks)
  bracketOnError (openTempFile "." "temp")
    (\(tempName, tempHandle) -> do
      hClose tempHandle
      removeFile tempName
    )
    (\(tempName, tempHandle) -> do
      hPutStr tempHandle newTodoItems
      hClose tempHandle
      removeFile fileName
      renameFile tempName fileName
    )