ghcを初めて使う場合のメモ

あらすじを立ててみるテスツ。

遅延評価だとかモナドとかの用語は先につかわない。

Hello World

まずはHello World。ソース:Main.hs

module Main (main) where

-- hello world
main = putStrLn "Hello World"

ビルド

$ ghc --make Main.hs -o hello

これで実行ファイルhelloが出来上がる。Mainモジュールのmainがエントリーポイントになる。

コード中で、--出始まる行はコメントです。はじめのmodule Main (main) whereほ内訳は、Mainがモジュール名、(main)がエクスポートする名前のリストになってる。複数あるなら(main, foo, bar)のようにする。

$と()

文字列を二つ出してみる

module Main (main) where

-- このコードはコンパイルエラです
main = putStrLn ["Hello", "World"]

とリストに書いてビルドするとエラーが出る

Coudn't match `Char' against `[Char]'
  Expected type: Char
  Inferred type: [Char]
In the list element: "Hello"
In the first argument of `putStrLn', namely `["Hello", "Wrold"]'

リストの中身が「文字」であってほしいのに「文字のリスト」があるから困ってるという感じのエラーです。

ここでshow関数を使えば多くの型を文字列にしてくれるようです

module Main (main) where

main = putStrLn (show ["Hello", "World"])

これでビルドが通るし、実行すれば["Hello", "World"]と出力されます。

かっこが必要なのは、関数が左に強く結びつくからのようです。このかっこを外すとビルドエラーが出ますがそれはputStrLnにshowを引数で渡そうとするから起こるエラーのよう。

関数を適用した結果を関数に渡すようなコードが連続すると、かっこだらけになるのですが、かっこを消す方法として二項演算子の$を使っているようです。

module Main (main) where

main = putStrLn $ show ["Hello", "World"]

この$自体は何のこともない関数で f $ x = f xなので、前後が1変数づつなら$なしでも同じですが、結合順位が普通の関数適用より弱いので、使う場所を工夫すれば、かっこが必要なくなります。つまり foo (bar (buzz arg))は、foo $ bar $ buzz argと書けてしまう。

とはいえ$でかっこが消せるのは最後の引数だけだけど、Haskellコードでこの$はけっこう使われているっぽい。


do

コマンドラインパラメータを表示するコードを書いてみる。

コマンドライン引数を取り出す関数は、SystemモジュールのgetArgsがそれっぽい

mainに組み込んでみます(これはビルド不可)

module Main (main) where

import System

-- このコードはコンパイルエラです
main = putStrLn $ show getArgs

このエラーはNo instance for (Show (IO [String]))です。showは[String]には使えるので、この邪魔なIOを外す必要が出てきます。

そこはdoを使うとよいようです

module Main (main) where

import System

main = do
  args <- getArgs
  putStrLn $ show args

これはビルドが通ってきちんとコマンドライン引数が表示されます。

IO〜な戻り値がでる関数はdoの中で<-に変数を入れてあげて、後ろでその変数を使うことになります。

関数

リストの内容を一行づつ出すようにします。
まず繰り返す部分を関数にして再帰的に呼び出させます。

module Main (main) where

import System

main = do
  args <- getArgs
  putStrEachLine args

putStrEachLine strs = case strs of 
  [] -> return ()
  head : tail -> do
    putStrLn head
    putStrEachLine tail

まず引数をcaseで条件分岐させます。空リスト[]の場合はreturn ()、何か入ってるときはputStrLnして残りを再帰的に呼び出します。

明示的にcaseを使わずはじめからパターン分岐で定義もできます

module Main (main) where

import System

main = do
  args <- getArgs
  putStrEachLine args

putStrEachLine [] = return ()
putStrEachLine (head : tail) = do
    putStrLn head
    putStrEachLine tail

要かっこ

モジュール分割

このputStrEachLineを別モジュールに書いてみます

MyLibs/Print.hs

module MyLibs.Print (putStrEachLine) where

putStrEachLine [] = return ()
putStrEachLine (head : tail) = do
    putStrLn head
    putStrEachLine tail

Main.hs

module Main (main) where

import System
import MyLibs.Print

main = do
  args <- getArgs
  putStrEachLine args

ビルドは同様に

$ ghc --make Main.hs -o hello

Main.hsで使ってるライブラリもタイムスタンプを見てビルドしてくれます

ライブラリ単体をコンパイルする場合は、

$ ghc -c -O MyLibs/Print.hs

Preludeとか

mapMなどで置き換え...

データ型

カレンダーを作る。。。