梅酒2012

焼酎を用意しただけ。仕事から帰ってきたら漬かってた。

1339881248

竜宮、奄美、朝日と今年は全て黒糖焼酎。

去年は砂糖を700g/Lで作ったらちょっと酸っぱかったので、今年は900g/Lにしてみたらしい(1Kg/Lは甘すぎる)。

1339881250

3ヶ月後からのお楽しみ。

アプリカティブとしての関数とReaderモナド

すごいHaskellを読んでみて、結局理解があやふやなままなのがタイトルの2つ(というか似たようなもんなので実際は一つかな)だ。使い方はわかるけどなんでそうなんのかなー的なモヤモヤ感が残りまくりだ。

ProductName すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社 / 2940円 ( 2012-05-23 )


定義(p.247)だと

pure x = (\_ -> x)
f <*> g =\x -> f x (g x)

ってなってて、pureはいいんだけど、なんでfは二引数取るようになってんだよと(Readerモナドも一緒)。 本書の例のとおりに

(+) <$> (+3) <*> (*100)

を考えた場合

Prelude Control.Applicative> :t ((+3) <*> (*100))

<interactive>:1:12:
    Occurs check: cannot construct the infinite type: a0 = a0 -> b0
    Expected type: (a0 -> b0) -> a0
      Actual type: (a0 -> b0) -> a0 -> b0
    In the second argument of `(<*>)', namely `(* 100)'
    In the expression: ((+ 3) <*> (* 100))

は、普通にエラー。まぁ実際そうだし、fってこれだよなぁ

Prelude Control.Applicative> :t ((+) <$> (+3)) 
((+) <$> (+3)) :: Num a => a -> a -> a

p.249の例を見るとつないでるっていうよりは分配してる感しか得られないんだよなぁ。

Prelude Control.Applicative> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
[8.0,10.0,2.5]

120616 追記

bind(>>=)ってIOが強く印象づけられてたのでb順番に計算を実行するための枠組みだと思っていたが、別に計算を連鎖させるわけじゃないのかな。

より大きい計算を作るための計算戦略の枠組みを提供するだけで。

PyccoをHaskellに対応させておいた

Pyccoを使ってソースコードリーディングすると、ソースとコメントが一緒に管理されるので調子いいんだけど、Haskellに対応してなかったのでやっといた。

今は出力した静的なドキュメントを単純にリンクしてるだけなんだけど、手間がかかるのでgithubにpushした時点でよろしくやってくれるようにしたいなぁと思っているんだが、いい方法が思い浮かばない。

モナド変換子の使い方がわかってきた

モナドの上にまたモナドって株か?二階建てか?なんて思って敬遠してたのだけど、なんとなくわかってくると、包んで包んでしてるだけじゃないかと。

RWH18章

モナド変換子を使わないでログ記録。これはIO [(FilePath, Int)]型になる。

module CountEntries (listDirectory, countEntriesTrad) where

import System.Directory (doesDirectoryExist, getDirectoryContents)
import System.FilePath ((</>))
import Control.Monad (forM, liftM, when)

listDirectory :: FilePath -> IO [String]
listDirectory = liftM (filter notDots) . getDirectoryContents
    where notDots p = p /= "." && p /= ".."

countEntriesTrad ::FilePath -> IO [(FilePath, Int)]
countEntriesTrad path = do
  contents <- listDirectory path
  rest <- forM contents $ \name -> do
                          let newName = path </> name
                          isDir <- doesDirectoryExist newName
                          -- when isDir $ countEntriesTrad newName
                          if isDir
                             then countEntriesTrad newName
                             else return []
  return $ (path, length contents) : concat rest

続いてモナド変換子(WriterT)を使う。WriterT [(FilePath, Int)] IO () という型は一見威圧されて目を背けたくなるところだが、単にIO箱を更にWriterT箱に包みつつそっちの箱に記録を取っていると考えればいいだけ。

元のcountEntriesTradと新しいcountEntriesを見比べてみるとliftIOっていうIOの箱の梱包を解いて中身を取り出す関数が増えているくらいだというのがわかる。

module CountEntriesT (listDirectory, countEntries) where

import CountEntries (listDirectory)
import System.Directory (doesDirectoryExist)
import System.FilePath ((</>))
import Control.Monad (forM_, when)
import Control.Monad.Trans (liftIO)
import Control.Monad.Writer (WriterT, tell)

countEntries ::FilePath -> WriterT [(FilePath, Int)] IO ()
countEntries path = do
  contents <- liftIO . listDirectory $ path
  tell [(path, length contents)]
  forM_ contents $ \name -> do
                          let newName = path </> name
                          isDir <- liftIO . doesDirectoryExist $ newName
                          when isDir $ countEntries newName

countEntriesでwhenを使っていたのでcountEntriesTradでも使えるかなと思ったんだけど、型が合わなくて無理だった。ソース見ると

when              :: (Monad m) => Bool -> m () -> m ()
when p s          =  if p then s else return ()

-- | The reverse of 'when'.

unless            :: (Monad m) => Bool -> m () -> m ()
unless p s        =  if p then return () else s

と定義されているのでIO ()型の時しか使えないのね。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan
オライリージャパン / 3990円 ( 2009-10-26 )


おまけ

IO剥がすのになんでliftIOって言うんだろ?liftっていうのが自分の直感的なイメージに反するのだけど。

Haskellで九九

文脈を読む感じで。

Prelude Control.Applicative> [(x*) | x <- [1..9]] <*> [1..9]

リスト内包表記が操作してる感を醸し出しているので排除する。

Prelude Control.Applicative> (*) <$> [1..9] <*> [1..9]

2つのリストから値を取り出しては、可能な組み合わせ全てに対して掛け算をする。

Prelude Control.Applicative> :t ((*) <$>)
((*) <$>) :: (Functor f, Num a) => f a -> f (a -> a)

Prelude Control.Applicative> :t [1..9] <*> [1..9]
[1..9] <*> [1..9]
  :: (Enum a, Enum (a -> b), Num a, Num (a -> b)) => [b]

さらに、これをliftAを使って書き直せば

Prelude Control.Applicative> liftA2 (*) [1..9] [1..9]

これは、掛け算っていう演算をアプリカティブの文脈に持ちあげておいて、リストに適用するっていう理解でいいのかな。

Prelude Control.Applicative> :t liftA2 (*)
liftA2 (*) :: (Num c, Applicative f) => f c -> f c -> f c

ProductName すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社 / 2940円 ( 2012-05-23 )


EmacsでHaskellをつかうために

flymakeがあるのが当たり前になってしまっているので、Haskellで書いてる時にもダメなところはダメって言ってもらわないとちょっと気持ち悪い。

Haskellでもflymakeを使えるようにするためにMac/emacsでHaskell環境を作るを参考に自分のflymake-init.elに追加した。

ついでにhaskell-modeも新しいのを入れるためにgit cloneしておいた。

おまけ

Real World Haskell 3周目

すごいHaskellですっかりHaskellにスイッチしてしまったので、一年半ぶりにReal World Haskellを読んでみることにした。

理解がいまいちなとこにポストイットを貼り付けておいたので、すごいHaskell読んだ後の僕がどんだけ剥せるかちょっと楽しみではある。

1288005286

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan
オライリージャパン / 3990円 ( 2009-10-26 )


ProductName すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社 / 2940円 ( 2012-05-23 )


それから、HackageでPDBtoolsってのを見かけたのであとでソースコードを読んでみる。

ソースコードリーディングするならHackageで探すのがいいのか、それともGitHubがいいのか

yesodをos10.7にインストール(解決)

yesodをインストールしようとしたら、エラーを吐いた。(Haskell Platform 2012.2.0.0 for Mac OS X, 32 bit)

$ cabal install yesod-platform
Resolving dependencies...
Configuring ranges-0.2.4...
Preprocessing library ranges-0.2.4...
Building ranges-0.2.4...
[1 of 1] Compiling Data.Ranges      ( Data/Ranges.hs, dist/build/Data/Ranges.o )

Data/Ranges.hs:9:6:
    Illegal datatype context (use -XDatatypeContexts): Ord a =>
cabal: Error: some packages failed to install:
email-validate-0.2.8 depends on ranges-0.2.4 which failed to install.
ranges-0.2.4 failed during the building phase. The exception was:
ExitFailure 1

rangesを別にインストールしてみたらもうちょい詳しいエラーが出た。

$ cabal install ranges-0.2.4
Resolving dependencies...
Configuring ranges-0.2.4...
Preprocessing library ranges-0.2.4...
Building ranges-0.2.4...
[1 of 1] Compiling Data.Ranges      ( Data/Ranges.hs, dist/build/Data/Ranges.o )

Data/Ranges.hs:9:6:
    Illegal datatype context (use -XDatatypeContexts): Ord a =>
cabal: Error: some packages failed to install:
ranges-0.2.4 failed during the building phase. The exception was:
ExitFailure 1

120613

ranges-0.2.4をダウンロードしてきて

runghc Setup configure --prefix="$HOME/.cabal" --user
runghc Setup build
runghc Setup install

って普通にいれたら入った。

あとは再度上の手順でyesodをインストールした。

今日の畑(120610)

サツマイモの間に撒いておいた枝豆の種が芽を出していた。

1339500541

1339500539

キュウリの花が咲いていた。まぁ、順調なのかな。

1339500543

ゴーヤはポット苗をさらに2つほど定植した。

1339500545

今年は去年の倍のゴーヤを植えた。

最近飲んだの

葵天下は静岡の日本酒らしく、結構攻めてくる感がある。香りもそれなりにするしね。

1339315193

四季桜は栃木の酒らしく、ちょっとまとわりつく感じ。まぁあそこら辺は濃い味付けの物が多いからなぁ。

1339315234