「本物のプログラマはHaskellを使う」をこなしていく。 第ニ回
前回定義したrepeatedをもう一回使う。
Prelude> let repeated f n = \x -> (iterate f x) !! n Prelude> :type repeated repeated :: (a -> a) -> Int -> a -> a Prelude> let f1 = \f n -> repeated f n 12 Prelude> :t f1 f1 :: Num a => (a -> a) -> Int -> a Prelude> let f2 = \f n -> repeated f n (12::Double) Prelude> :t f2 f1 :: (Double -> Double) -> Int -> Double
:type とやると関数でもなんでも型がわかる。:type repeated したときに表示される a というのは特に指定なしでCharでもIntでも良い的な意味。次の f1 では、Num a となっているので、a の型はNumに固定される。f2 では、DoubleからDoubleへの変換を行う関数と、Intを引数に取り、Doubleを返す。と読み取れる。他にも :t (++"hogehoge") とか、:t String、:t ++ などをやって型というのに親しんでいく感じ。ココらへんは大体わかるので淡々とこなしていく。引き続き、Data.ByteString.Char8 のモジュールを読み込んで、[Char] と ByteString の違いをrepeated関数を使って見るんだけど、自分の知識不足故、ByteStringの何がどう便利なのかよくわからなかった。モジュールを読み込んで使う、という練習なのかな?
次は、Control.Exception モジュールを読み込んでfinallyを使ってみようというサンプル。型の「IO a」「IO()」というのを見るとモナドという正体不明の何かを想起させるため、アレルギーから理解を拒むようになる。それを見なかったことにして、粛々とサンプルを真似て打ち込んでいく。printThenAdd というのを定義するが、ここがなんかポイントっぽい気もする。return がモナドを返すというのもそれっぽい。が、ここでは気にせずに進もう。
Prelude> let printThenAdd v = do {x <- v; print x; return (x+1)} Prelude> > :t printThenAdd printThenAdd :: (Num b, Show b) => IO b -> IO b Prelude> printThenAdd 1 <interactive>:53:1: Non type-variable argument in the constraint: Num (IO b) (Use FlexibleContexts to permit this) When checking that ‘it’ has the inferred type it :: forall b. (Num b, Num (IO b), Show b) => IO b Prelude> printThenAdd (return 1) 1 2 Prelude> :i return class Applicative m => Monad (m :: * -> *) where ... return :: a -> m a ... -- Defined in ‘GHC.Base’ Prelude> :t return return :: Monad m => a -> m a
で、最後にrepeated と printThenAdd をhsファイルに書いて、それを読み込んでみる。なお、GHC7.10.1では、記事のとおりだとコンパイルが通らず、ちょっと変更が必要。こんな感じになる。
import Data.ByteString.Char8 import Control.Exception repeat :: (a -> a) -> Int -> a -> a repeat f n = \x -> (iterate f x) !! n printThenAdd :: (Num a, Show a) => IO a -> IO a printThenAdd v = do {x <- v; print x; return (x+1)}
今日のところは、こんなもんでおしまい。