未来を創る、テックコミュニティー

Get Programming with Haskell②

草場代表
2020/11/27

こんばんは。代表の草場です。

Haskell触ります。「Get Programming with Haskell」についてです。まずは、Lesson 1. Getting started with Haskellからです。

1.4. ハスケルコードを使ったライティングと作業

Haskellの基本的なI/Oはかなり高度なトピックです。プログラミング言語を勉強する際、、途中で出力を確認しながらやるのが一般的ですが、Haskellでは、この種のアドホックなデバッグは通常不可能です。この問題を複雑にしているのは、Haskellの素晴らしいコンパイラがあなたのコードの正しさにも厳しいということです。

Haskellは、プログラムを実行する前に時間をかけて問題を考え抜くことを強く求めています。Haskellを使って経験を積めば、このようなフラストレーションがこの言語のお気に入りの機能のいくつかになると確信しています。

フラストレーションを最小限に抑えて Haskell のコードを書くコツは、コードを少しずつ書いていき、書いているうちに各ビットをインタラクティブにいじることです。
これを実演するために、乱雑なHaskell プログラムを、それぞれの部分を理解しやすいようにきれいにします。著者から読者に感謝のメールを送るコマンドラインアプリを書いてみましょう。以下、プログラムの最初の下手くそなバージョンです。

messyMain :: IO()
messyMain = do
    print "Who is the email for?"
    recipient <- getLine
    print "What is the Title?"
    title <- getLine
    print "Who is the Author?"
    author <- getLine
    print ("Dear " ++ recipient ++ ",\n" ++
    "Thanks for buying " ++ title ++ "\nthanks,\n" ++
    author )

このコードは、messyMain という名前の関数に全て入っていることが問題です。

モジュール式のコードを書くのが良い習慣であるというアドバイスは、ソフトウェアの世界ではかなり普遍的なものですが、Haskellでは、理解してトラブルシューティングできるコードを書くことが不可欠です。

このプログラムは、messyMain の名前を main に変えれば、コンパイルして実行することができます。first_prog.hsと同じディレクトリにあると仮定して、このコードをそのままGHCiにロードすることもできます。

$ghci
GHCi> :l first_prog.hs
[1 of 1] Compiling Main ( first_prog.hs, interpreted )
Ok, modules loaded: Main.

GHCiは、メイン関数を持っていても気にしないことに注意してください。これで、あなたのコードをテストドライブに持っていくことができます。

GHCi> messyMain
"Who is the email for?"
Happy Reader
"What is the Title?"
Learn Haskell
"Who is the Author?"
Will Kurt
"Dear Happy Reader,\nThanks for buying Learn Haskell\nthanks,\nWill Kurt"

このコードを分解して、もっと簡単に作業ができるようにします。今回の目的は電子メールを作成することです。電子メールは、受信者セクション、本文、署名の3つの部分を結びつけることで構成されています。まず、これらの部分をそれぞれの関数に引っ張り出します。以下のコードを first_prog.hs ファイルに記述します。

toPart recipient = "Dear" ++ recipient ++ ",\n"

これをテストするために、GHCi でファイルを再度ロードします。

GHCi> :l "first_prog.hs"
[1 of 1] Compiling Main ( first_prog.hs, interpreted )
Ok, modules loaded: Main.
GHCi> toPart "Happy Reader"
"DearHappy Reader,\n"
GHCi> toPart "Bob Smith"
"DearBob Smith,\n"

これをGHCiに読み込むと、Dearと受信者の名前の間のスペースが欠けているという、エラーがでます。これを修正する方法は、

toPart recipient = "Dear " ++ recipient ++ ",\n"

GHCiに戻り、

GHCi> toPart "Jane Doe" "Dear Jane Doe,\n"

他の2つの関数を定義します。

bodyPart bookTitle = "Thanks for buying " ++ bookTitle ++ ".\n"
fromPart author = "Thanks,\n"++author

GHCi> bodyPart "Learn Haskell"
"Thanks for buying Learn Haskell.\n"
GHCi> fromPart "Will Kurt"
"Thanks,\nWill Kurt"

あとは、すべてを結びつける関数が必要です。

createEmail recipient bookTitle author = toPart recipient ++
                                         bodyPart bookTitle ++
                                         fromPart author

3つの関数呼び出しの配置に注目です。Haskellでは、重要なホワイトスペースの使用は制限されています。ほとんどのエディタは、Haskellプラグインを使って自動的にこれを処理することができます。すべての関数を書いたら、createEmailをテストします。

GHCi> createEmail "Happy Reader" "Learn Haskell" "Will Kurt"
"Dear Happy Reader,\nThanks for buying Learn Haskell.\nThanks,\nWill Kurt"

クリーンアップされたメインで改善された first_prog.hsが以下です。

main = do
     print "Who is the email for?"
     recipient <- getLine
     print "What is the Title?"
     title <- getLine
     print "Who is the Author?"
     author <- getLine
     print (createEmail recipient title author)

コンパイルの準備ができていますが、最初にGHCiでテストします。

GHCi> main
    "Who is the email for?"
     Happy Reader
    "What is the Title?"
     Learn Haskell
     "Who is the Author?"
     Will Kurt
     "Dear Happy Reader,\nThanks for buying Learn Haskell.\nThanks,\nWill Kurt"

最後に、プログラムをコンパイルすることができます。

$ ghc first_prog.hs
[1 of 1] Compiling Main             ( first_prog.hs, first_prog.o )
Linking first_prog ...
$ ./first_prog
"Who is the email for?"
Happy Reader
"What is the Title?"
Learn Haskell
"Who is the Author?"
Will Kurt
"Dear Happy Reader,\nThanks for buying Learn Haskell.\nThanks,\nWill Kurt"

まとめ

Haskell プラットフォームには、Haskell のコンパイラである GHC、Haskell の対話型インタプリタである GHCi、そしてビルドツールである stack が含まれています。
Haskell プログラムの書き方、リファクタリング、インタラクション、コンパイルの基本について説明しました。

この記事を書いた人
草場代表
エディター