岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

Haskell初心者の僕が試したことをまとめてみた

なぜHaskellに興味をもったのか

僕がHaskellに興味をもったきっかけは、『まつもとゆきひろ コードの世界?スーパー・プログラマになる14の思考法』(通称「ホスト本」)でした。

ホスト本の第14章「関数型プログラミングについてなど」では、関数型プログラミングの特徴が4つ挙げられています。

  • 関数そのものをデータとして捉える(ファーストクラス関数)
  • 関数を引数として取る高階関数
  • 同じ引数を取る関数は同じ結果となる参照透明性
  • 参照透明性を実現するための副作用の忌避

このうち「参照透明性」が特に興味深く感じました。「同じ引数を取る関数は同じ結果となる」のですから、きっとテストが書きやすいはずです。「状態」によって結果が変わるオブジェクト指向言語ではテストが書きづらいと日頃から思っていたので、この参照透明性は福音のように感じられました。

その「参照透明性を実現するための副作用の忌避」が徹底されている言語がHaskellです。第14章では「Haskellのプログラムには基本的には副作用がありません」と書かれています。同章で紹介されている関数型言語には他にLispOCamlErlangがありますが、LispOCamlには副作用があります。Erlangは並列計算に特化しているため、今の僕の興味からは外れます。

以上のような理由で、Haskellに興味をもちました。

ちなみに、同じくまつもとさんが書かれた「総論 複数のプログラミング言語を学ぶ意義 - フリー言語で真のプログラミングを学ぶ:ITpro」では、Haskellが「世界を広げる言語」と評価されています。

GHCCentOSにインストールする手順

まずは環境作りということで、「HowTo:ProgrammingEnvironment」に従って、HaskellコンパイラであるGHCを手元のCentOS5に入れてみました。手順は下記の通りです。

$ wget http://haskell.org/ghc/dist/6.12.2/ghc-6.12.2-i386-unknown-linux-n.tar.bz2
$ tar xvf ghc-6.12.2-i386-unknown-linux-n.tar.bz2
$ cd ghc-6.12.2
$ ./configure
$ sudo make install

続いて、パッケージ管理用のコマンドであるcabalを入れました。事前にgmp-develとzlib-develのインストールが必要でした。

$ sudo yum install gmp-devel
$ sudo yum install zlib-devel
$ wget http://hackage.haskell.org/packages/archive/cabal-install/0.8.2/cabal-install-0.8.2.tar.gz
$ tar xvf cabal-install-0.8.2.tar.gz
$ cd cabal-install-0.8.2
$ sh bootstrap.sh

cabalのインストール後、「$HOME/.cabal/bin」にPATHを通しました。

Haskellプログラミングの参考にした本

文法の勉強には『ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門』が役立ちました。「cat」や「head」といったUNIXコマンドをHaskellで模してみるところから始まるため、先日『UNIXという考え方―その設計思想と哲学』を読んだ僕にはうってつけの内容でした。

第9章以降は難しかったので流し読みしました。必要になったらちゃんと読もうと思っています。

じつは先に『プログラミングHaskell』を買い、途中まで読んでいたのですが、挫折しました。僕には難しく感じられたためです。「ふつける」を読んだ今なら、読めるようになっているかもしれません。

初めてのHaskellプログラミングのお題

いよいよ、初めてのHaskellプログラミングです。AmazonProduct Advertising API利用時に必要な処理を題材に選びました。

Product Advertising APIの利用時には、パラメータ文字列と秘密キーでHMAC SHA-256を計算し、結果をbase64エンコードした署名が必要になります。詳しくは「アマゾンAPIを使うのに2009年8月15日から認証が必要になるらしい」をご参照ください。

この「パラメータ文字列と秘密キー」を引数に取り、「HMAC SHA-256を計算し、結果をbase64エンコードした署名」を返す関数を、Haskellで書いてみようという魂胆です。テストファーストが理想なのですが、早く書きたくて仕方がなかったので、実装から始めました。

試行錯誤の末、できたのが下記のコードです(いまひとつ自信がありません。おかしな点に気づかれたら、ぜひご指摘ください)。

module Main (main) where

import System.Environment (getArgs)
import Data.ByteString.Lazy.Char8 (pack)
import Data.Digest.Pure.SHA (hmacSha256, bytestringDigest)
import Data.ByteString.Lazy (unpack)
import Codec.Binary.Base64 (encode)

main :: IO ()
main = do
  args <- getArgs
  let secret_key = if length args >= 1 then args !! 0 else ""
  message <- getContents
  putStrLn $ sign secret_key message

sign :: String -> String -> String
sign secret_key message = encode $ unpack $ bytestringDigest $ hmacSha256 (pack secret_key) (pack message)

signが目的の関数です。ライブラリを利用しているだけですが、適切なライブラリを探すだけで一苦労でした。

追記(2010-07-26)

「適切なライブラリ」を書いてませんでしたね。dataencSHAです。

ちゃんと動くの?

上記のコードをコンパイルするには、必要なライブラリをcabalでインストールする必要があります。手順は下記の通りでした。

$ cabal update
$ cabal install dataenc
$ cabal install SHA

コードを「sign.hs」として保存、コンパイルして実行してみます。

$ ghc --make -o sign sign.hs
$ echo -n "GET\
>
> webservices.amazon.com\
>
> /onca/xml\
>
> AWSAccessKeyId=00000000000000000000&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06" | ./sign 1234567890
Nace+U3Az4OhN7tISqgs1vdLBHBEijWcBeCqL5xN9xg=

Amazon アソシエイト(アフィリエイト) - ヘルプ」で示されている例と同じ文字列が得られたので、計算結果は問題なさそうです。

今後の予定

以上で、所期の目的が達成できました。今後は以下の内容に挑戦しようと思っています。