Old/sampou.org/cut-sea

cut-sea


Haskell再入門


Haskellの関数合成でひっかかってたこと

まずここでやりたいのは

h(x,y) = f(g(x,y))

だそうだ。ただし、これはHaskellコードではない。つまり(x,y)はタプルじゃなくて、 他言語によくある関数引数のリストを表現しているもの。 つまりやりたいのは

h x y = f $ g x y

というhをfとgで表現したい、ということだ。

コンビネータのつくりかた

大抵の場合、コンビネータを作ること自体はさして難しくない。 このblogの場合だと、

h f g x y = f $ g x y

これで出来上がりだ。 hはfとgを合成するという点に注目して、中値演算にすればさらにいい感じになる場合もある。

(<+-) f g x y = f $ g x y

こうすれば(+2) <+- (*)というのがいきなり書けるようになる。
ここでgが3引数だろうと4引数だろうと同じ要領で書けることにも注目してもらいたい。

point freeにしてみる

blog主はこいつをpoint freeで書こうとしている。 blogでの出発点はh x y = f $ g x yなんだけど、別にそれでもかまわない。 ここからpoint freeにしてみよう。

h x y = f $ g x y
      ↓
h x = f . g x

ここまでは問題ない。 blog主もここまでは迷わずできてる。 ここで次の形にするためには省略された括弧を付けてみるとよい。

h x = (f .) (g x)

これが分からない人は、以下の順で考えると良い。

h x = f . (g x)
      ↓
h x = (.) f (g x)
      ↓
h x = ((.) f) (g x)
      ↓
h x = (f .) (g x)

どうかな? (f .)をf’とでもすればもう分かるはず。

h x = f' $ g x
      ↓
h = f' . g
      ↓
h = (f .) . g

というわけでめでたしめでたし・・・

ちょっと待った! 本当にそれでいいの?そんなんで納得しちゃっていいのか?

確かに変換は分かった。 それで正しく動作することも確認できた。
しかし、これはコンパイルされたコードのように、人間が読んだり書いたりするにはちょっと無理があるんじゃないか? という声が出てきてもよさそうに思う。

h x y = f $ g x y

はっきり言って最初のこのコードの方がずっと読みやすいし、変更もしやすそう!って初心者なら思うはず。

h = (f .) . g

このコードはどう読みゃいいのさ!?

point freeの思考

まずよく知ってるつもりになっているBコンビネータ、つまり(.)について考察してみよう。 型だけで議論する。(但し(.)の実装は当然知ってて欲しい。)

(.) :: (b -> c) -> (a -> b) -> a -> c

Bコンビネータは通常 f . g のようにして使う。 ここでは(f .)という最初の引数だけを適用したものを考えよう。

f :: b -> c
(f.) :: (a -> b) -> a -> c

fの型がb -> cなら(f.)は(a -> b) -> a -> cとなる。 型における(->)が右結合であることに注意して括弧を補完すると以下の形になる。

(f.) :: (a -> b) -> (a -> c)

つまり、(f.)は(a -> b)な関数を(a -> c)な関数に変換する。 要は返り値がちょっと違う版の関数に変換するのだ。 bとcとの間の変換はfが取り扱う。
いくつか例を挙げると

((map toUpper).) :: 文字列を返すような関数を大文字で返す版にする
(length .) :: リストを返すような関数をリストの長さを返す版にする
(const .) :: 1引数の関数を2引数の版にする(2番目の引数はダミー)

などなど。

このように(.)に左からfを適用すると最終的に得られる型を変えた版の関数を生成することができるわけだが、 さらにもう一度適用したらどうなるだろう?

((f.).) :: ???

メモ用紙でコリコリやればすぐに分かる。 (f.)の型は(a -> b) -> (a -> c)なので、(a1 -> (a -> b)) -> (a1 -> (a -> c))となる。 やはり型における(->)は右結合なので、それぞれの内側の括弧ははずせる。

((f.).) :: (a -> a1 -> b) -> (a -> a1 -> c)

というわけで、((f.).)は2引数関数の返り値の型を変えた版を生成する。 あとはもう分かると思うんだけど、結論から言えば、(.)を右からn回適用すればn引数の関数の返り値をfで変換する版の関数を生成できてしまうのだ。

一般化「gしてfする」

Bコンビネータをpoint freeで思考する人は「gしてfする」というのが出てくるとすぐさま「f . g」が出てくる。 よく言われるようにUNIX系コマンドのフィルタのようなイメージも同時に浮かぶようだ。(但しデータの流れは右から左に抜けるけどね) しかしgの引数の数に対して一般化するとこうだ。

「gしてfする」は((..(f.)..).) gである。
ただし、fの後ろの(.)の数はgの引数の数と同じだけ噛ます。

だから変換の末に(f.).gが得られたのではなく、「gしてfする」がいきなり(f.).gと書けてしまうのだ。

(<+-) f g = (f.). g

もちろんこの慣用句が身についていると大抵この手の合成は新たに作成するという発想にすらならない。
今回のblog postの場合にはfは2を足し、gは2引数を掛けていた。 考え方としては「掛けてから2を足す」なので、(+2) . (*) で筋としてはほぼ良い。 ただgにある関数が2個の引数を取るので、「掛けてから2を足す」は((+2).) . (*)となる。 というわけで、この思考に慣れてくると変換の末に到達するのではなく、いきなり思考をそのまま書き下す形でpoint freeな式が出てくるようになる。

(.g)は?

さて、(f.)を考察し始めた時にすでに気になっているだろうと思うので、今度は(.g)を考えてみよう。 つまり今度は(.)の第2引数であるgだけを適用したものが何を表現しているのか考える。

g :: a -> b
(.g) :: (b -> c) -> (a -> c)

というわけでご想像通り、今度は入力となる引数の型を変換した版の関数を生成してくれる。 (.)を知っていればそれほど驚く結果ではないはずだ。 ところが(f.)の時のように(.)を複数回適用しようとすると困る。

(.(.g)) :: ((a -> c1) -> c) -> ((b -> c1) -> c)

残念ならが左への(.)適用の繰り返しはそうそう直感的に分かりやすいものではなくなる。 しかし、ここで再び右側に(.)を追加するものを考えると状況が変わる。

(.g) :: (a -> c) -> (a1 -> c)
((.g).) :: (a1 -> a -> c) -> (a1 -> a2 -> c)
(((.g).).) :: (a1 -> a2 -> a -> c) -> (a1 -> a2 -> a3 -> c)

どうだろう?
これは(.g)の右に(.)をn回適用すると(n+1)番目の引数の型をちょっと変えた版の関数を生成してくれるのだ。

times3 x y z = x * y * z

という関数があった時に、ちょっと引数の型を変えた版を作ってみる。

ghci> (.(+2)) times3 1 2 3
18
ghci> ((.read).) times3 1 "2" 3
6
ghci> (((.length).).) times3 1 2 [1..4]
8

複数の引数にそれぞれちょっとした変換を加えたい?

??? :: (a1 -> a2 -> a3 -> c) -> (a1' -> a2' -> a3 -> c)

これも問題ない。

ghci> ((.(+2)).((.read).)) times3 1 "2" 3
18

任意個の引数を取る関数の任意の引数をちょっと変更するような関数も簡単だ。 最後のは3引数の関数に対して、「2番目の引数をreadで変換して1番目の引数を2水増ししてから計算するような版に変換」という思考から直接書き下せる。

というわけで、Bコンビネータのやっていることを考えると当然と言えば当然だが、 関数の引数や返り値を軽くラップした亜種版くらいは使い捨てのように生成できるようになる。
これをわざわざトップレベルに定義した方がよいのか、使い捨てで良いのかについては実際に書いているコードによると思う。 一度しか使わないのであれば私は大抵使い捨てる。 名前を考えるのが面倒だからというただそれだけの理由だけど。

最後に

とりあえずここでは(.)の慣用句について紹介したけど、 point freeでザクザク書ける関数プログラマはこの手の慣用句をいくつか知っていて、 それで当たり前のようにpoint freeで書き下しているようだ。(多分)

そもそも、なぜpoint freeで考えるのか? そのメリットについては、また別の話なのでそのうち気が向いたら書く。 cut-sea:2011/10/05 18:39:36 JST


Yesod_Internationalization

少し試してみたけど簡単に使えたっぽい。 どんどん便利になっていくなぁ。 いいわYesod


Yesod

0.6.0をインストールしてみた。 こっちね -> Yesod


よくわかりません

誰だか存じ上げませんでした。
すごく分かりやすいです。
こういう風に順を追ってちゃんと説明できる人好きだな。

あ、ちなみに今のリンク先はあんま関係ないです。 過去のYコンビネータの説明やらなんやらです。cut-sea:2010/04/07 14:03:58 JST


Applicative

typeclassopediaを読んでて、そういや(<*>)をSとして使ってた自分。 朝食摂取しながらサンドウィッチの包みで検証したのでメモっておく。

Applicative fのfが型(->)aの時

(<*>)::f (b->c)->f b->f c

     →(((->) a) (b->c))->(((->) a) b)->(((->) a) c)
     →(a->(b->c))->(a->b)->(a->c)
     →(a->b->c)->(a->b)->(a->c)

pure::b->f b

    ::b->(((->) a) b)
    ::b->(a->b)
    ::b->a->b

つまり、(<*>)がSでpureがK! Applicative ((->)a)はSKコンビネータをプリミティブオペレータとする合成戦略を実現している。

あと

fmap g x = pure g <*> x

pure g <*> x = (<*>) (pure g) x

             = S (K g) x
             = (S (K g)) x
             = (B S K g) x
             = B S K g x
             = (B S K) g x

こいつが fmap g xと同じになるので、fmap g x = (B S K) g xなのでfmapはまさにBSKとなっている。

Kがpure(=return)ってのは確かに、プログラムを書いていて、 引数を一個消して一階層上の関数合成のレイヤに持ち込んだときにできるDSLのconstとなるのと感覚が非常に合致している。 なんか少しスッキリ。cut-sea:2010/03/31 10:15:21 JST

ちなみにBはおなじみ(.)に相当するので、 fmap = B S K = (.) <*> return

             = (<*>) . return

つまり、「fmapはreturnして(<*>)すること」と読める。 まんまです。

さらにスッキリ。cut-sea:2010/03/31 10:25:02 JST


グローバル変数が欲しい理由?

これについてちょっと考えてみやう!
こっちね -> グローバル変数が欲しい理由?


Real World Haskell

今週いただきました。 amazonで予約したやつがまだ来てないのに。
しかし分厚いなー。 やっぱ楽しいや。
Haskellの本ってこれまでどうも学術的と思われるような本ばっかだったので、 この本は良いよー。 まさにPracticalな感じでほんきに存在意義があると思う。 この泥臭さが好きだなー。cut-sea:2008/12/05 23:53:29 JST


GEB

ゲーデル・エッシャー・バッハね。 ものまね鳥を一個スタックに積んでこちらを読む。

ものまね鳥で階数の話が出てて、 だからどうしたと思ってスルーしたりもしたが、 早速それっぽい所にヒットしたか。cut-sea:2008/11/25 22:21:03 JST


GHC-HEAD

しばらくぶりに追っかけ。
とおもったらエラーが発生。
patchが無いと言われた。

え?Ubuntuってデフォでpatch入ってないの?

しょうがないのでパッケージシステムからインストールして、再度ビルド中。 今度はちゃんと最後まで行くんだろうか。cut-sea:2008/11/12 22:57:50 JST

  • いった。6.11.20081111になってる。ってことは11/11版か、ホヤホヤだな。cut-sea:2008/11/13 09:54:45 JST

ものまね鳥をまねる(20)

やっぱりことあるごとに他人にアピールしておくものだ。
声を掛けてた人が区の図書館から、この本を入手して「カリーの森」は難しくなかったよ言って少し説明してくれた。 分ったわけじゃないけど、とりあえず予想通り、ゲーデルとか数学の世界で起った数学基礎論とか、 歴史的な経緯とかの知識があった方が分りやすいだろうね、ということに。
で、その人が言うには「ゲーデル・エッシャー・バッハ」が一番分りやすかったとのことで、 あのイエローページみたいなやつかと少し萎え萎えなんだが、読んでみるかなぁと。
ちなみにその人は数学科出身。cut-sea:2008/11/12 22:16:32 JST


ものまね鳥をまねる(19)

カリーの森で足止め。
議論のあたりを誰か説明してくれー。cut-sea:2008/10/21 10:00:35 JST


ものまね鳥をまねる(18)

なんと、ようやくスマリヤンがなぜこのような比喩を使ったのかが解った。
この様な、というのは「鳥に呼び掛けたら鳥の名前を応える」というやつ。

「ものまね鳥をまねる」でググるとamazonを除けば 背徳的なものまね鳥なんかが結構最初の方に引っ掛かって、 私もその方が何か分りやすそう、とか思ってたんだけどカリーの森でナゾが解ける。

残念ながら「鳥が交尾して鳥を産む」という比喩はヒッジョーにマズい。 これは破綻しちゃうんだ。スゲー!やっぱちゃんと考えられた比喩だったんだ。cut-sea:2008/10/15 10:09:25 JST


ものまね鳥をまねる(17)

激ムズ。
少し調べてみたんだけど、こんな難問出すなー。cut-sea:2008/10/14 10:17:41 JST


ものまね鳥をまねる(16)

賢人鳥の章も終えた。
結構これもK的な簡単さがあったな。 問題も読んだ瞬間になんだか自明というか、 単に言い換えてるだけというか、 そういう簡単な感じがする。

で、今朝はそのままカリーの森に突入。
まだはっきりとはしないけど、コンビネータにその森特有の意味を持たせて、 ロジックを築いていく(?)という応用編に突入ということでいいかな? 第IV部ということで、ここで部がかわったし。cut-sea:2008/10/10 09:59:37 JST


ものまね鳥をまねる(15)

Oが面白い。
へたすりゃ賢人鳥Θより興味深いかも。
ちなみに、ΘはΘx=x(Θx)ね。OはOxy=y(xy)である。cut-sea:2008/10/09 13:46:37 JST


ものまね鳥をまねる(14)

賢人鳥系の問題をはじめてるけど、最初はとっかかりが無くてなかなか解けなかった。
再帰してて自分自身が右辺にも来ちゃってるから、 以前、解いた「任意の鳥が好きな鳥」というやつの表現を使うというのは思いついたんだけど、 そのパターンが多くてノートを漁っても、なかなかすぐ見つけらんなくて解けない。 また少しずつ慣れてはきてるけど。 なんか章が変わる度に毎度振り回されてる気がする。cut-sea:2008/10/08 20:31:05 JST


unfoldr

今日仕事で「こりゃHaskellならunfoldrだよな」って思ったんだけど、 まぁC#でforeachを使いまくってゴテゴテと書いていた。

で、帰ってからHaskellでポイントだけ再実装してみたんだけど、unfoldrだと引数が一個足りない??

module Main where

import Data.List

type ProductsId = String
type Quantity = Int
type PackLimit = Int

data OrdersProducts = Op {prod :: ProductsId, q :: Quantity} deriving (Show)

original_order :: [OrdersProducts]
original_order = [Op "A" 5, Op "B" 7, Op "C" 3, Op "D" 12, Op "E" 2, Op "F" 2, Op "G" 1]

unfoldr2 :: (b -> c -> Maybe(a,b,c)) -> b -> c -> [a]
unfoldr2 f b c =
    case f b c of
      Just (a,b',c') -> a : unfoldr2 f b' c'
      Nothing        -> []

makePlan :: PackLimit -> [OrdersProducts] -> [[OrdersProducts]]
makePlan limit = unfoldr2 breaker []
    where
      breaker [] [] = Nothing
      breaker wk [] = Just (wk,[],[])
      breaker wk (r:rs) | qt == limit = Just (wk++[r],[],rs)
                        | qt <  limit = breaker (wk++[r]) rs
                        | qt >  limit = Just (wk++[Op (prod r) (q r-over)],[],Op (prod r) over:rs)
          where
            qt = foldr ((+).q) 0 wk + q r
            over = qt - limit

あーそーだ。

makePlan :: PackLimit -> ([OrdersProducts],[OrdersProducts]) -> [[OrdersProducts]]
makePlan limit = unfoldr breaker
    where
      breaker ([],[]) = Nothing
      breaker (wk,[]) = Just (wk,([],[]))
      breaker (wk,(r:rs)) | qt == limit = Just (wk++[r],([],rs))
                          | qt <  limit = breaker ((wk++[r]),rs)
                          | qt >  limit = Just (wk++[Op (prod r) (q r-over)],([],Op (prod r) over:rs))
          where
            qt = foldr ((+).q) 0 wk + q r
            over = qt - limit

これでいんじゃん。アホだ。

それよか内部定義の型とかって調べられたりしないかな。 例えば

*Main> :t makePlan.breaker

みたいに。 トップレベルで実装してからwhere句に移動させるのタルいし。cut-sea

  • これって mapAccumL じゃね?
  • mapAccumLでも可能だけど、unfoldrだねって結論が出た。(w
  • いや、これは mapAccumL でしょ。breaker から breaker を呼ぶのはいただけないです。 – [1..100]>>=pen

    makePlan :: PackLimit -> [OrdersProducts] -> [[OrdersProducts]]
    makePlan limit rs = concat $ snd $ mapAccumL f ([],0) $ map Just rs ++ [Nothing]
      where
        f (wks,_) Nothing               = (([],0),[wks])
        f (wks,t) (Just r) | t' < limit = ((wks++[r],t'),[])
                           | otherwise  = ((wks',b),ws)
          where
            t' = t + q r
            (a,b) = divMod t' limit
            ws = [wks++[r{q=limit-t}]] ++ replicate (a-1) [r{q=limit}]
            wks' = if b == 0 then [] else [r{q=b}]
  • 読みやすさを追求するなら,パスを分けるという手もあるかな...nobsun

    makePlan :: PackLimit -> [OrderedProducts] -> [[OrderedProducts]]
    makePlan n = map (compo . group) . splits n . concatMap decompo
    
    splits :: Int -> [a] -> [[a]]
    splits n = unfoldr phi
      where phi [] = Nothing
            phi xs = Just $ splitAt n xs
    
    decompo :: OrderedProducts -> [ProductId]
    decompo (Op i q) = replicate q i
    
    compo :: [[ProductId]] -> [OrderedProducts]
    compo = map $ uncurry Op . (head &&& length)

ものまね鳥をまねる(13)

今朝、無事通過。
そのまま勢いで「賢人鳥のギャラリー」に突入。 ざっと先読みして太字を追うと、 1階の鳥とか固有鳥とか正規結合子とか類似性とか拡張的とかまばらであるとか 今迄の空気じゃないタームが並んでいて楽しみだ。cut-sea:2008/10/01 22:00:09 JST


ものまね鳥をまねる(12)

やられた。
これまでも誤字はあったものの、読んでて気付くレベルだった。 まさか問題が間違ってるとはなぁ。
長く悩んでどうにも降参しちゃって答を見たんだが、直感的にはその形になる様に思えず、 答から逆算してみたら案の定問題が間違ってやがった。
今日は1問すら進まなかったよ。 今朝の段階で今の章が終わると思ってたのに。cut-sea:2008/09/30 22:40:07 JST


ものまね鳥をまねる(11)

なるほど。
当り前なんだけど、重複している引数を1つに消し込みたかったら、 やはり重複効果のある鳥を使えばよいんだ。cut-sea:2008/09/24 12:39:11 JST


ものまね鳥をまねる(10)

今日から重複効果のある鳥なんだけど、以前より使って良い鳥が増えた分、むしろ簡単かもしれん。
それでいて、自明っぽい問題じゃないから面白い。 なんとなく、よりハイレベルのユーティリテュがあるおかげで以前より難しいはずの問題がさらっと解ける感じDeath。

だけど、ある鳥を別の鳥で表現することにあまり意味を感じなくなってきた。
ちょっと誤解をまねきそうなので補足すると、ある鳥がB系とT系で表現できるとか M系を必要とするかなどを理解する(ちゅーか直感的に判る感覚を身に付ける)のはいいと思うんだけど、 だからと言ってRを使って表現したかCを使って表現したかなどはどーでもいーよなーっとか。

ただ手を動かして解くことでたまに発見することや理解したと思えることがちょこちょこ出てくるので、 パスるのも良くないよなーってことで、ちまちまと問題と格闘してしまうのであった。cut-sea:2008/09/22 10:40:59 JST


ものまね鳥をまねる(9)

今朝ようやく多数の鳥が登場する章をくぐり抜けたのであった。 最初戸惑ったflip系の変換も慣れると意外とサクサク解ける。 それでも少し頭を捻らなきゃならないレベルだったので楽しめた。

で、来週から何かと言うと「ものまね鳥、ウグイス、ホシムクドリ」というわけで重複効果ですな。 つまりこれで結合、置換、複製の各パターンが出揃うわけだ。 その次に来るのは「賢人鳥のギャラリー」ってことで、再帰系だ。 Yだけはすでに出てきてたけど、再帰パターンになる鳥の仲間だっけかな。(テキトー)

そこまででようやく基本のキが終わるようなもんだろうか。長ぇ。cut-sea:2008/09/20 00:16:15 JST


Gtk2HS

今度は最近完成したとのアナウンスが流れ、amazonでも予約できるようになっているReal World Haskellで紹介されているGtk2HS。

とりあえず片っ端からパッケージでlib系を入れておいてからINSTALLドキュメントに従ってインストールしたらサンプルプログラムが結構サクッと動作した。

こっちのがドキュメントやらサンプルやらがあっていいかも。cut-sea:2008/09/18 03:16:30 JST


wxHaskell

HaskellでGUIを書こうとした場合に何が最短だろうか?と調べたらwxHaskellっぽい?
開発が止まってるという話を聞いたんだけど、今もソースは更新されてるみたいなのでいいんじゃないかしらん。 とりあえずwxcoreとwxをインストールしたら無事サンプルが動作。
wxWidgetを2.8が最新だったもんだから最初それを使ってたんだけど、どうにもエラーが出てまともに起動せず。 wxHaskellの方のchangesを見たら2.6への適合はやったとあるが、それが最後の模様。 というわけで2.6にダウングレードしてwxcore/wxの再インストールをした次第。cut-sea:2008/09/17 23:42:06 JST


ものまね鳥をまねる(8)

自分が導出した式と回答にある式の相互変換をするために少し時間がかかったけど、うまくつながった。
解けるとどうということはないが、案外気付かないものかも。
これまでも何度か経験したことではあるんだけど、 見えないものが見えるようになった瞬間というのは結構うれしい。cut-sea:2008/09/17 07:00:08 JST


ものまね鳥をまねる(7)

B系の結合鳥をやってて、これは結構簡単だったんで油断してた。
この2、3日はRとかCとかFなどの置換鳥をやってるんだが、これが結構難しい。

コツらしきものを掴んだと思ったら次の問題ではうまくいかないし、 これは答だろうと思って到達しても回答にあるのと形が違ってると、 さらにそれが対応するのか確認しないとどうも気持ち悪いしってことでやってるんだが、 結合鳥の時と違って結構対応確認も手間取るしで、遅々として進まない。

多分全体から見ると今は一番つまらない箇所なんだろうけど、 それでも結構楽しんでやれているのは幸いだ。
まだまだ問題がいっぱいあるので、いつこの森を抜けられるのか分らんが、 パズルを解くのが直接の目的じゃないのでじっくりやる。cut-sea:2008/09/13 23:50:59 JST


ものまね鳥をまねる(6)

ようやく大勢の鳥たちがぞくぞくと登場する章に突入。 さっそくルリツグミB登場。 今朝解いた問題は以前証明したものをBも利用して再証明するということで、 だいぶパターンに慣れてきた感じ。cut-sea:2008/09/05 10:03:39 JST


ものまね鳥をまねる(5)

ようやく最初の章がおわった。

ここまでで登場した鳥は、M,I,L,K。(ミルク?)

難しいので、解けない問題は答を見て、翌日再チャレンジでやってる。 KとかIの問題は簡単なんだが、MとLが絡む問題は思いつく必要がある(今のところ?)ことがあって、 どうにも解けなかったりする。
トータルで今までのところ3問くらいは自力で解けなかったかな。

とりあえず今日ようやく賢人鳥が登場した。
賢人鳥はいわゆるYコンビネータ。
本書の比喩では「xの名を呼びかけるとxが好む鳥の名を語って答えてくれる鳥」。。。
なんだけど、これって本当に答えてくれてるのか??

で、ここは1問だけなんだけど、問題に取りかかる前に、早速MとLの関係に悩み中。cut-sea:2008/09/03 10:25:24 JST


ものまね鳥をまねる(4)

今朝は結構すんなり2問目が解けた。 すんなりっていってもメモ用紙3枚くらいに色々書いてゴミにしたけど。

あとTo Dissect a Mockingbirdってのが結構いい。 印刷して読んでみているところ。cut-sea:2008/08/13 10:27:57 JST


ものまね鳥をまねる(3)

ざっと一通りは読んだが、読んだだけーなので、 今度は改めて問題を解いていく。
手を動かして進めましょうということでノートを購入。

朝食を取りながら一問目に挑戦するも…。orz

やっぱムズイ。cut-sea:2008/08/12 10:09:08 JST


ものまね鳥をまねる(2)

少しずつ、おもしろくなってきた。
SとKってなんかイビツな基本の結合子だと思ってたけど、 なるほど、そういうことかとナットク。
いろんな結合子が無数に作れる中で、 引数の複製と消滅という特異な機能をもっているってのがまず第一にある模様。 じゃあ他のでもいいじゃんとなりそうだが、実際そうらしく、 何を基本の結合子として全てを表現するかという試みは、 それはそれでいくつものパターンでやっているみたい。 しかしその中でなぜSとKがこうも有名なのかというのは、まだちょっと分ってない。cut-sea:2008/07/25 02:34:23 JST


ものまね鳥をまねる

入手。
昨日GaucheFestから帰ったら来てた。 さっそく読みはじめたが、こりゃ紙とエンピツが必需品だな。 ひさびさに筆記用具をとりだしてちまちまとはじめたが、 裏紙じゃなくて、ノートが欲しくなってきた。どしよ。cut-sea:2008/07/21 00:01:56 JST


GaucheFest

なんだけどHaskellで書いてた。
逆にnobsunはGauche?で。

とりあえず(<*>)と(&&&)を使った処理をfirst sketchでポイントフリー的に考えられるようになりつつある。
ただ、考え方それ自体はともかく、実際にパラった時に複数の流れの どれがどうなってたか一度に頭の中に置いておけないんだよなぁ。

(<*>) :: (a->b->c) -> (a->b) -> (a->c)
(f <*> g) x = f x $ g x

(<.>) :: (a->b) -> (a->c) -> (a->(b,c))
(f <.> g) x = (f x, g x)

利用するコンビネータとしてB以外では上記の2つを用意。 上のは(<*>)=Sで下のは(&&&)でControl.Applicativeにあるけど、 今回はとりあえず自作した。 (<.>)としたのは、あとから(&&&)がそうだったんだと知っただけ。

で、

(<->) :: Gate -> Gate -> Gate
f <-> g = g.((++).fst<*>snd).f

(<|>) :: Gate -> Gate -> Gate
f <|> g = (((++).fst<*>fst.g.snd)<.>(snd.g.snd)).f

こんな感じのコードを以前だったらリファクタリングの結果として到達することはあったけど、 なんとか考えながらではあるものの書き下せるようになってきてる。

上記のは一応

infixr 4 <|>
infixr 5 <->
infixr 6 <.>
infixr 7 <*>

としてる。 cut-sea:2008/07/19 17:38:54 JST


GHCのビルド(続^3)

インストールしてみて分ったけど、ghc-pkg listがえらく不足してた。

tar zxvf ~/POOL/Haskell/ghc-6.8.3-src.tar.bz2
tar zxvf ~/POOL/Haskell/ghc-6.8.3-src-extralibs.tar.bz2

の両方を展開しておいてから./configure+mk/config.mk編集+gmakeで

[email protected]> ghc-pkg list
/usr/local/lib/ghc-6.8.3/package.conf:
    Cabal-1.2.4.0, HUnit-1.2.0.0, QuickCheck-1.1.0.0, array-0.1.0.0,
    base-3.0.2.0, bytestring-0.9.0.1.1, cgi-3001.1.6.0,
    containers-0.1.0.2, directory-1.0.0.1, fgl-5.4.2.0,
    filepath-1.1.0.0, (ghc-6.8.3), haskell-src-1.0.1.2,
    haskell98-1.0.1.0, hpc-0.5.0.1, html-1.0.1.1, mtl-1.1.0.1,
    network-2.2.0.0, old-locale-1.0.0.0, old-time-1.0.0.0,
    packedstring-0.1.0.0, parallel-1.0.0.1, parsec-2.1.0.1,
    pretty-1.0.0.0, process-1.0.0.1, random-1.0.0.0,
    regex-base-0.72.0.1, regex-compat-0.71.0.1, regex-posix-0.72.0.2,
    rts-1.0, stm-2.1.1.1, template-haskell-2.2.0.0, time-1.1.2.1,
    unix-2.3.0.1, xhtml-3000.2.0.0

なレベルでなかなかよさげ。 あとは自分でHackageDBから持ってくるか。 ただ、インストール先がどうにも気に入らないので、そこを調べてからかなぁ。cut-sea:2008/07/04 07:52:57 JST


GHCのビルド(続々)

./configureした後にmk/config.mkを手でいじった。

#-----------------------------------------------------------------------------
# GMP Library (version 2.0.x or above)
#
HaveLibGmp      = YES
LibGmp          = installed

GMP_INCLUDE_DIRS=/usr/pkg/include
GMP_LIB_DIRS=/usr/pkg/lib

あまりよくわかってないけど、上記のように編集してからgmakeすると以下のようにgmpを一応リンクしてくれた。

ghc-6.8.3:
        -lc.12 => /usr/lib/libc.so.12
        -lutil.7 => /usr/lib/libutil.so.7
        -lm.0 => /usr/lib/libm387.so.0
        -lm.0 => /usr/lib/libm.so.0
        -lgmp.3 => /usr/pkg/lib/libgmp.so.3
        -lrt.0 => /usr/lib/librt.so.0
        -lpthread.0 => /usr/lib/libpthread.so.0

ああ、ちなみにbuildしたやつをinstall済みなので、 現在buildに使われているghcはもうghc-6.8.3になってる。cut-sea:2008/07/02 07:56:29 JST


GHCのビルド(続)

/usr/local/include以下にgmp.hを入れたり、gmpを先にビルドしてinstallしておいてからやったりしたけどそれはダメだった。

その後、いくつかいじってしまったので 現在再確認のためまたまた条件を絞ってビルド中だけど、 今朝起きたらビルドできてたっぽいのでメモ。

GHC-6.8.3のビルドをするにあたり、システムには動くGHCが必要なわけだけど、 そいつが私の環境の場合にはpkgsrcというNetBSDのパッケージシステムから入れたものである。 具体的にはghc-6.8.2という直前のversionが入っているので、そいつを利用してビルドすることになるんだと思う。

で、まずそのGHCについてなんだけど、 /usr/pkg/lib/ghc-6.8.2というディレクトリにGHCのすべてのファイルが入っているようだ。

例えば/usr/pkg/bin/ghcなんかは/usr/pkg/lib/ghc-6.8.2/ghc-6.8.2というexeにシンボリックリンクされているし、 /usr/pkg/lib/ghc-6.8.2/incldueやらlibやらがある構成になっている。

ただし、pkgsrcでビルドした際にどうやらgmpはgmpで別にインストールしているから、 GHCとしてはそいつを利用する方向でビルド&インストールしたんだろう。
正しい姿がどうなのか知らないけど、 gmpを別のpkgsrcからインストールされたものを 利用したせいじゃないかと思うんだけど /usr/pkg/lib/ghc-6.8.2/include配下にgmp.hが無いというのが直接の問題のようだ。
つまり、ここにgmp.hがあれば良いとうことのようで、 ln -s /usr/pkg/include/gmp.h /usr/pkg/lib/ghc-6.8.2/include/gmp.hとして おいてやるとビルドに成功するというオチっぽい (これだけなのかどうかを現在再確認中)。

  • ちなみにこれでビルドしたやつをインストールすると/usr/local/lib/ghc-6.8.3/incldueの下にはめでたくgmp.hが入るので次回からは大丈夫なのかな?? 本体に肝心のlibgmp.soがリンクされてないっぽいけど。。。
  • shelarcy(2008/07/01 20:55:07 JST): gmp は処理系自身の実装ではなくて、 Integer 型を実装するための効率のよい組み込み演算の提供に使われています。なので、Integer 型を使わない限り、libgmp.so はリンクされていなくても構いません。

これなら./configure;gmakeで問題ないみたいだ。cut-sea:2008/06/28 08:00:31 JST

とりあえずビルド自体はおっけーそうだ。 ちなみにmake installもgmake installじゃないと通らなかった。これは初めての体験かも。 しかし足りない。 pkgsrcから入れたのが

[email protected]> ldd /usr/pkg/lib/ghc-6.8.2/ghc-6.8.2 
/usr/pkg/lib/ghc-6.8.2/ghc-6.8.2:
        -lc.12 => /usr/lib/libc.so.12
        -ltermcap.0 => /usr/lib/libtermcap.so.0
        -lreadline.5 => /usr/pkg/lib/libreadline.so.5
        -lutil.7 => /usr/lib/libutil.so.7
        -lm.0 => /usr/lib/libm387.so.0
        -lm.0 => /usr/lib/libm.so.0
        -lgmp.3 => /usr/pkg/lib/libgmp.so.3

        -lrt.0 => /usr/lib/librt.so.0
        -lpthread.0 => /usr/lib/libpthread.so.0
[email protected]> 

に対して

[email protected]> ldd ghc-6.8.3 
ghc-6.8.3:
        -lc.12 => /usr/lib/libc.so.12
        -lutil.7 => /usr/lib/libutil.so.7
        -lm.0 => /usr/lib/libm387.so.0
        -lm.0 => /usr/lib/libm.so.0
        -lrt.0 => /usr/lib/librt.so.0
        -lpthread.0 => /usr/lib/libpthread.so.0

である。 –with-gmp-includesと–with-gmp-librariesの両オプションを付けても結果は同じ。 termcapとreadlineはともかくgmpが見られてないのはどうもマズいんでないの?と思うんだけどどうなんでしょう?cut-sea:2008/06/28 09:21:35 JST


GHCのビルド

うまくいくかどうかはわからんがメモっていく。

  1. darcsリポジトリからtarballを取得。 現時点では最新が ghc-HEAD-2008-06-06-ghc-corelibs-testsuite.tar.gz なのでそれ。 testsuiteの入ってないのとか圧縮形式の違うのがあるが、とりあえず一番でかいのを戴いてくる。
  2. tar zxvf ghc-HEAD-2008-06-06-ghc-corelibs-testsuite.tar.gzするとghcというディレクトリができる。_darcsというサブディレクトリもできているようなのでdarcsリポジトリからpullしてきた状態になってんだろう。
  3. cd ghc;darcs pull -aとする。多分さらに新しい分があったらpatchを持ってくるのかな。
  4. chmod +x darcs-allとするが、こいつはperlスクリプトらしい。どうも

    #! /usr/bin/perl -w

    となってるので

    #! /usr/pkg/bin/perl -w

    と修正しておく。

  5. ./darcs-all pull -aとしてみるとなんか進行しているっぽい。

    [email protected]> ./darcs-all pull -a
    == running darcs pull -a
    Pulling from "http://darcs.haskell.org/ghc"...
    This is the GHC darcs repository (HEAD branch)
    
    For more information, visit the GHC developer wiki at
      http://hackage.haskell.org/trac/ghc
    **********************
    No remote changes to pull in!
    == nofib not present or not a repository; skipping
    == running darcs pull -a --repodir testsuite
    Pulling from "http://darcs.haskell.org/testsuite"...
    This is the GHC testsuite darcs repository (HEAD branch)
    
    For more information, visit the GHC developer wiki at
      http://hackage.haskell.org/trac/ghc
    **********************
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/array
    Pulling from "http://darcs.haskell.org/packages/array"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/base
    Pulling from "http://darcs.haskell.org/packages/base"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/bytestring
    Pulling from "http://darcs.haskell.org/packages/bytestring"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/Cabal
    Pulling from "http://darcs.haskell.org/packages/Cabal"...
    This is GHC's branch of the main Cabal repo.
    
    NB. DO NOT push new Cabal patches to this repo, instead use the main Cabal repo at
      http://darcs.haskell.org/cabal
    
    Patches to this repo must pass GHC's validate script before being pushed.
    **********************
    No remote changes to pull in!
    == running darcs pull -a --repodir libraries/containers
    Pulling from "http://darcs.haskell.org/packages/containers"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/directory
    Pulling from "http://darcs.haskell.org/packages/directory"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/editline
    Pulling from "http://darcs.haskell.org/packages/editline"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/filepath
    Pulling from "http://darcs.haskell.org/packages/filepath"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/ghc-prim
    Pulling from "http://darcs.haskell.org/packages/ghc-prim"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/haskell98
    Pulling from "http://darcs.haskell.org/packages/haskell98"...
    No remote changes to pull in!
    == running darcs pull -a --repodir libraries/hpc
    Pulling from "http://darcs.haskell.org/packages/hpc"...
    No remote changes to pull in!
    == running darcs pull -a --repodir libraries/integer-gmp
    Pulling from "http://darcs.haskell.org/packages/integer-gmp"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/old-locale
    Pulling from "http://darcs.haskell.org/packages/old-locale"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/old-time
    Pulling from "http://darcs.haskell.org/packages/old-time"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/packedstring
    Pulling from "http://darcs.haskell.org/packages/packedstring"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/pretty
    Pulling from "http://darcs.haskell.org/packages/pretty"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/process
    Pulling from "http://darcs.haskell.org/packages/process"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/random
    Pulling from "http://darcs.haskell.org/packages/random"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/template-haskell
    Pulling from "http://darcs.haskell.org/packages/template-haskell"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/unix
    Pulling from "http://darcs.haskell.org/packages/unix"...
    Finished pulling and applying.
    == running darcs pull -a --repodir libraries/Win32
    Pulling from "http://darcs.haskell.org/packages/Win32"...
    No remote changes to pull in!
    [email protected]> 

    ずらずらとこんな感じでしばらくしたら終了。

  6. ./darcs-all –extra getとしたらさらになんか取得しているっぽい。

    [email protected]> ./darcs-all --extra get
    warning: adding --partial, to override use --complete
    warning: array already present; omitting
    warning: base already present; omitting
    warning: bytestring already present; omitting
    warning: Cabal already present; omitting
    warning: containers already present; omitting
    warning: directory already present; omitting
    warning: editline already present; omitting
    warning: filepath already present; omitting
    warning: ghc-prim already present; omitting
    warning: haskell98 already present; omitting
    warning: hpc already present; omitting
    warning: integer-gmp already present; omitting
    warning: old-locale already present; omitting
    warning: old-time already present; omitting
    warning: packedstring already present; omitting
    warning: pretty already present; omitting
    warning: process already present; omitting
    warning: random already present; omitting
    warning: template-haskell already present; omitting
    warning: unix already present; omitting
    warning: Win32 already present; omitting
    == running darcs get --partial http://darcs.haskell.org/packages/ALUT
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/GLUT
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/HUnit
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/ObjectIO
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/OpenAL
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/OpenGL
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/QuickCheck
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/cgi
    Copying patch 3 of 3... done.
    Applying patch 2 of 2... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/fgl
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/haskell-src
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/html
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/mtl
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/network
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/parsec
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/parallel
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/regex-base
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/regex-compat
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/regex-posix
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/stm
    Copying patch 3 of 3... done.
    Applying patch 2 of 2... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/time
    Copying patch 3 of 3... done.
    Applying patch 2 of 2... done.
    Finished getting.
    == running darcs get --partial http://darcs.haskell.org/packages/xhtml
    Copying patch 2 of 2... done.
    Applying patch 1 of 1... done.
    Finished getting.
    [email protected]> 
  7. ./darcs-all –testsuite getをしたけど。

    warning: adding --partial, to override use --complete
    warning: testsuite already present; omitting
    warning: array already present; omitting
    warning: base already present; omitting
    warning: bytestring already present; omitting
    warning: Cabal already present; omitting
    warning: containers already present; omitting
    warning: directory already present; omitting
    warning: editline already present; omitting
    warning: filepath already present; omitting
    warning: ghc-prim already present; omitting
    warning: haskell98 already present; omitting
    warning: hpc already present; omitting
    warning: integer-gmp already present; omitting
    warning: old-locale already present; omitting
    warning: old-time already present; omitting
    warning: packedstring already present; omitting
    warning: pretty already present; omitting
    warning: process already present; omitting
    warning: random already present; omitting
    warning: template-haskell already present; omitting
    warning: unix already present; omitting
    warning: Win32 already present; omitting
    [email protected]> 

    すでにあるから端折ったよってことらしい。

  8. ./darcs-all –nofib getもoptionalなんだけどやっておく。

    [email protected]> ./darcs-all --nofib get
    warning: adding --partial, to override use --complete
    == running darcs get --partial http://darcs.haskell.org/nofib
    This is the nofib darcs repository (HEAD branch)
    
    For more information, visit the GHC developer wiki at
      http://hackage.haskell.org/trac/ghc
    **********************
    Copying patch 3 of 3... done.
    Applying patch 2 of 2... done.
    Finished getting.
    warning: array already present; omitting
    warning: base already present; omitting
    warning: bytestring already present; omitting
    warning: Cabal already present; omitting
    warning: containers already present; omitting
    warning: directory already present; omitting
    warning: editline already present; omitting
    warning: filepath already present; omitting
    warning: ghc-prim already present; omitting
    warning: haskell98 already present; omitting
    warning: hpc already present; omitting
    warning: integer-gmp already present; omitting
    warning: old-locale already present; omitting
    warning: old-time already present; omitting
    warning: packedstring already present; omitting
    warning: pretty already present; omitting
    warning: process already present; omitting
    warning: random already present; omitting
    warning: template-haskell already present; omitting
    warning: unix already present; omitting
    warning: Win32 already present; omitting
    [email protected]>
  9. cd gmp; tar zxvf gmp-4.2.1.tar.gz; cd ..としておく。
    解凍してなければソースツリーにあるのは使われないってことらしいので、 解凍しておいてやる。実際/usr/pkgのものを使ってもらわなくてもいい。 /usr/pkg配下はpkgsrcからのインストールでガチガチに管理されてっから、 へたにそっち見られるよか自前で持ってるやつを使ってもらうのが望ましい。 ってことでやってみたら魔の時間帯を切り抜けたっぽい。gmakeして8minくらいでgmp.hがねぇって言われてたから。
  10. sh bootでいよいよbuild開始かしらん。

    [email protected]> sh boot
    Booting .
    configure.ac:904: warning: AC_CACHE_VAL(fp_gcc_version, ...): suspicious cache-id, must contain _cv_ to be cached
    ../../lib/autoconf/general.m4:1973: AC_CACHE_VAL is expanded from...
    ../../lib/autoconf/general.m4:1993: AC_CACHE_CHECK is expanded from...
    aclocal.m4:548: FP_HAVE_GCC is expanded from...
    configure.ac:904: the top level
    configure.ac:904: warning: AC_CACHE_VAL(fp_gcc_version, ...): suspicious cache-id, must contain _cv_ to be cached
    ../../lib/autoconf/general.m4:1973: AC_CACHE_VAL is expanded from...
    ../../lib/autoconf/general.m4:1993: AC_CACHE_CHECK is expanded from...
    aclocal.m4:548: FP_HAVE_GCC is expanded from...
    configure.ac:904: the top level
    configure.ac:904: warning: AC_CACHE_VAL(fp_gcc_version, ...): suspicious cache-id, must contain _cv_ to be cached
    ../../lib/autoconf/general.m4:1973: AC_CACHE_VAL is expanded from...
    ../../lib/autoconf/general.m4:1993: AC_CACHE_CHECK is expanded from...
    aclocal.m4:548: FP_HAVE_GCC is expanded from...
    configure.ac:904: the top level
    Booting libraries/ALUT
    Booting libraries/GLUT
    Booting libraries/OpenAL
    Booting libraries/OpenGL
    Booting libraries/base
    Booting libraries/directory
    /usr/pkg/share/aclocal/rep.m4:7: warning: underquoted definition of AM_PATH_REP
    /usr/pkg/share/aclocal/rep.m4:7:   run info '(automake)Extending aclocal'
    /usr/pkg/share/aclocal/rep.m4:7:   or see http://sources.redhat.com/automake/automake.html#Extending-aclocal
    Booting libraries/editline
    Booting libraries/network
    Booting libraries/old-time
    Booting libraries/process
    Booting libraries/regex-posix
    Booting libraries/time
    Booting libraries/unix
    configure.ac:67: warning: AC_CACHE_VAL(cv_func_usleep_return_type, ...): suspicious cache-id, must contain _cv_ to be cached
    ../../lib/autoconf/general.m4:1973: AC_CACHE_VAL is expanded from...
    ../../lib/autoconf/general.m4:1993: AC_CACHE_CHECK is expanded from...
    configure.ac:67: the top level
    configure.ac:67: warning: AC_CACHE_VAL(cv_func_usleep_return_type, ...): suspicious cache-id, must contain _cv_ to be cached
    ../../lib/autoconf/general.m4:1973: AC_CACHE_VAL is expanded from...
    ../../lib/autoconf/general.m4:1993: AC_CACHE_CHECK is expanded from...
    configure.ac:67: the top level
    configure.ac:67: warning: AC_CACHE_VAL(cv_func_usleep_return_type, ...): suspicious cache-id, must contain _cv_ to be cached
    ../../lib/autoconf/general.m4:1973: AC_CACHE_VAL is expanded from...
    ../../lib/autoconf/general.m4:1993: AC_CACHE_CHECK is expanded from...
    configure.ac:67: the top level
    [email protected]> 

    いくつかwarningが出てるけど、errorとかではないんでいいのかな。

  11. gmakeとするGNU makeじゃないとダメらしい

    /usr/pkg/bin/ghc -optc-O -optc-Iparser -optc-I. -optc-O -H32m -O  -istage1/utils  -istage1/basicTypes  -istage1/types  -istage1/hsSyn  -istage1/prelude  -istage1/rename  -istage1/typecheck  -istage1/deSugar  -istage1/coreSyn  -istage1/vectorise  -istage1/specialise  -istage1/simplCore  -istage1/stranal  -istage1/stgSyn  -istage1/simplStg  -istage1/codeGen  -istage1/main  -istage1/profiling  -istage1/parser  -istage1/cprAnalysis  -istage1/iface  -istage1/cmm  -istage1/nativeGen -Wall -fno-warn-name-shadowing -fno-warn-orphans -Istage1 -cpp -fglasgow-exts -Rghc-timing -I. -Iparser -Iutil -fno-generics -package unix -ignore-package lang -Rghc-timing  -H16M '-#include "cutils.h"' -DUSING_COMPAT -i../compat -ignore-package Cabal -package directory -package pretty -package containers    -c parser/cutils.c -o stage1/parser/cutils.o
    In file included from /usr/pkg/lib/ghc-6.8.2/include/Stg.h:150,
                     from /usr/pkg/lib/ghc-6.8.2/include/Rts.h:19,
    
                     from parser/cutils.c:6:0: 
    
    /usr/pkg/lib/ghc-6.8.2/include/Regs.h:28:17:
         error: gmp.h: No such file or directory
    In file included from /usr/pkg/lib/ghc-6.8.2/include/Stg.h:150,
                     from /usr/pkg/lib/ghc-6.8.2/include/Rts.h:19,
    
                     from parser/cutils.c:6:0: 
    
    /usr/pkg/lib/ghc-6.8.2/include/Regs.h:121:0:
         error: expected specifier-qualifier-list before 'MP_INT'
    
    In file included from parser/cutils.c:6:0: 
    
    /usr/pkg/lib/ghc-6.8.2/include/Rts.h:203:0:
         error: expected ')' before '*' token
    
    /usr/pkg/lib/ghc-6.8.2/include/Rts.h:204:0:
         error: expected ')' before '*' token
    <<ghc: 27182068 bytes, 4 GCs, 159744/159744 avg/max bytes residency (1 samples), 16M in use, 0.00 INIT (0.00 elapsed), 0.05 MUT (0.37 elapsed), 0.02 GC (0.07 elapsed) :ghc>>
    gmake[1]: *** [stage1/parser/cutils.o] エラー 1
    gmake: *** [stage1] エラー 1
    [email protected]> 

    というわでエラーでストップ。 やっぱりgmp.hがみつからないと言われる。

    [email protected]> ll /usr/pkg/include/gmp.h
    -r--r--r--  1 root  wheel  82613 Apr 19 08:48 /usr/pkg/include/gmp.h
    [email protected]> 

    こいつが分らないのか、それともソースツリーにあるgmpディレクトリ下のが見つからないと言ってるのか(後者はないだろうと思うけど)。

  • これについては下記の指摘を受けてgmpを展開しておいて再挑戦。なんかうまく進行してたが今度も、

    /usr/pkg/bin/ghc -H32m -O  -istage1/utils  -istage1/basicTypes  -istage1/types  -istage1/hsSyn  -istage1/prelude  -istage1/rename  -istage1/typecheck  -istage1/deSugar  -istage1/coreSyn  -istage1/vectorise  -istage1/specialise  -istage1/simplCore  -istage1/stranal  -istage1/stgSyn  -istage1/simplStg  -istage1/codeGen  -istage1/main  -istage1/profiling  -istage1/parser  -istage1/cprAnalysis  -istage1/iface  -istage1/cmm  -istage1/nativeGen -Wall -fno-warn-name-shadowing -fno-warn-orphans -Istage1 -cpp -fglasgow-exts -Rghc-timing -I. -Iparser -Iutil -fno-generics -package unix -ignore-package lang -Rghc-timing  -H16M '-#include "cutils.h"' -DUSING_COMPAT -i../compat -ignore-package Cabal -package directory -package pretty -package containers    -c utils/Interval.hs -o stage1/utils/Interval.o  -ohi stage1/utils/Interval.hi
    <<ghc: 47759964 bytes, 7 GCs, 108544/159744 avg/max bytes residency (2 samples), 19M in use, 0.00 INIT (0.00 elapsed), 0.09 MUT (0.43 elapsed), 0.02 GC (0.04 elapsed) :ghc>>
    /usr/pkg/bin/ghc -optc-O -optc-Iparser -optc-I. -optc-O -H32m -O  -istage1/utils  -istage1/basicTypes  -istage1/types  -istage1/hsSyn  -istage1/prelude  -istage1/rename  -istage1/typecheck  -istage1/deSugar  -istage1/coreSyn  -istage1/vectorise  -istage1/specialise  -istage1/simplCore  -istage1/stranal  -istage1/stgSyn  -istage1/simplStg  -istage1/codeGen  -istage1/main  -istage1/profiling  -istage1/parser  -istage1/cprAnalysis  -istage1/iface  -istage1/cmm  -istage1/nativeGen -Wall -fno-warn-name-shadowing -fno-warn-orphans -Istage1 -cpp -fglasgow-exts -Rghc-timing -I. -Iparser -Iutil -fno-generics -package unix -ignore-package lang -Rghc-timing  -H16M '-#include "cutils.h"' -DUSING_COMPAT -i../compat -ignore-package Cabal -package directory -package pretty -package containers    -c parser/cutils.c -o stage1/parser/cutils.o
    In file included from /usr/pkg/lib/ghc-6.8.2/include/Stg.h:150,
                     from /usr/pkg/lib/ghc-6.8.2/include/Rts.h:19,
    
                     from parser/cutils.c:6:0: 
    
    /usr/pkg/lib/ghc-6.8.2/include/Regs.h:28:17:
         error: gmp.h: No such file or directory
    In file included from /usr/pkg/lib/ghc-6.8.2/include/Stg.h:150,
                     from /usr/pkg/lib/ghc-6.8.2/include/Rts.h:19,
    
                     from parser/cutils.c:6:0: 
    
    /usr/pkg/lib/ghc-6.8.2/include/Regs.h:121:0:
         error: expected specifier-qualifier-list before 'MP_INT'
    
    In file included from parser/cutils.c:6:0: 
    
    /usr/pkg/lib/ghc-6.8.2/include/Rts.h:203:0:
         error: expected ')' before '*' token
    
    /usr/pkg/lib/ghc-6.8.2/include/Rts.h:204:0:
         error: expected ')' before '*' token
    <<ghc: 27177664 bytes, 4 GCs, 159744/159744 avg/max bytes residency (1 samples), 16M in use, 0.00 INIT (0.00 elapsed), 0.04 MUT (0.65 elapsed), 0.02 GC (0.03 elapsed) :ghc>>
    gmake[1]: *** [stage1/parser/cutils.o] エラー 1
    gmake: *** [stage1] エラー 1
    [email protected]> 

    うーん。やっぱ見つからないっていわれる。時間がかかったのはgmpのbuildがはいったからかな。

  • shelarcy: ghc-<version>/gmpディレクトリ下のファイルが解凍されていなければ、ソースツリーにある gmp は使われていないはずです。エラーメッセージを元に調べてみましたが、どうやら gmp が一般的でない場所にインストールされていると gmp を発見できなくて ghc のビルドに失敗するようですね。configure の –with-gmp-includes オプションや –with-gmp-libraries オプションを使ってください。 http://www.nabble.com/building-against-gmp-in-a-nonstandard-location-td16075621.html
    • これは見て知ってたんですが、指定しても結局見つけてくれないんです。–with-gmp-includesでも–with-gmp-includes=/usr/pkg/include形でも結果は一緒。 ですが、gmpディレクトリの下のを解凍しておけばいいのですね?ということで解凍して試してみます。cut-sea:2008/06/22 11:18:54 JST
  • それと current (HEAD) のソースですが、testsuit や nofib (ベンチマーク)を必要としなかったり、本当の意味での最新版が欲しいのでなければ http://www.haskell.org/ghc/dist/current/dist/ から取得した方が楽です。こちらは既に sh boot まで適用済みなので、./configure & make だけでビルドできますし。


(Monad m) => (a -> m b) -> (a -> m c) -> (a -> m (b, c))

先日のは

(<*|*>) :: (Monad m) => (a -> c) -> (a -> m b) -> (a -> m (c, b))
g <*|*> f = uncurry (=<<) . ((return.).(,).g &&& f)

だったけど、今度は両方mつき。

(Monad m) => (a -> m b) -> (a -> m c) -> (a -> m (b, c))

これが望み。 ちゅーわけで実装したら、

(<*&*>) :: (Monad m) => (a -> m b) -> (a -> m c) -> (a -> m (b, c))
(<*&*>) f g x = f x >>= (\y -> (g x >>= (\z -> return (y , z))))

これが分りやすかったやつだけど、なんとかコンビネータつかって理解しようと思ったら

f <*&*> g = (=<<).((<*>).(((=<<).(return.).(,)).).(const id) <*> (const . g)) <*> f

まるでいみふめー。

さらに欲しいのは

(Monad m,Monad m1,Monad m2) => (a -> m1 b) -> (a -> m2 c) -> (a -> m (b, c))

だったんだけど、なんか頭イタ。

   m b >>= (return.).(,)
  /           /
a            /
  \         /
   m c >>= +

絵で書けばこんな感じっぽい? 最初のは

   m b >>= (return.).(,)
  /           /
a            /
  \         /
   c ------+

こんな感じだったと思えばいいのかな。 これ見ると最初のと同じくらいスッキリ書けてもいい気がしますが、どでしょ?

f :: a -> IO b
f = undefined

g :: a -> Maybe b
g = undefined

こうしておいて、

*FSDB> :t (return.).(,) =<< f
(return.).(,) =<< f :: (Monad m) => a -> m (IO b, a)

これはもう慣れたが、

*FSDB> :t ((return.).(,) =<< f) =<< g
((return.).(,) =<< f) =<< g :: a -> (IO b, Maybe b1)

うーん。これ行けるのか?

  1. g x が Maybe aになる
  2. a が渡ってきて a -> m (IO b, a)だったから m (IO b, Maybe a)

ってことか、ああ、なるほど。

  • いや、なるほどじゃねぇや。右の=<<から左に渡ってくるのが Maybe aのaだから a -> m (IO b, a)に適用されてやっぱ m (IO a, b)になるんでは? なぜぽ?
    さらに遡れば、((return.).(,) =<< f)の場合は f xがIO aになるわけだから、aが渡ってきて、も一つ引数もらってm (a,b)なモナドを返す関数になるのでは?cut-sea:2008/06/20 01:44:50 JST
  • とりあえず勘違い一個。

    (((return.).(,)) =<< f) 'c'

    これは((((return.).(,)) =<< f) ‘c’)であって

    (((return.).(,)) =<< f 'c')

    これは((((return.).(,)) =<< (f ‘c’))だから別だった。
    けど、そうだとしてもやっぱり何故(return.).(,)に渡ってくるのが生のbじゃなくてIO bなんじゃ??cut-sea:2008/06/20 01:56:13 JST

これが簡単にできるなら次は

(Monad m1, Monad m2, Monad m) => (m1 a, m2 b) -> m (a, b)

とかやってみたくなるな。
とか言いつつ眠いので今日はおしまい。cut-sea:2008/06/20 01:32:01 JST


&&&

(<*>)でなんとかしたいなと思ってる状況で、 片翼がMonadになってしまっているケースに遭遇。

どうなるかリファクタリングしたんだけど、書き方がどうにも納得出来ず、 というかイメージがさっぱりなので nobsunに相談したら次のコードを提示してくれた。

foo = uncurry (>>=) . (getModificationTime &&& (return.).(,))

一応収穫は(f.).gのイメージが出来た(上の(return.).(,)んとこ)ことと あと&&&の呪文を覚えたこと。
といってもこれもSの亜種だよなぁって話だけど。

*FSDB> :t (&&&)
(&&&) :: (Arrow a) => a b c -> a b c' -> a b (c, c')

あと

*FSDB> :t (***)
(***) :: (Arrow a) => a b c -> a b' c' -> a (b, b') (c, c')

なんてのもあるらしい。
あ、ghcのbuildはまだ。gmp.hが見つからないって叱られる。cut-sea:2008/06/19 08:47:45 JST

整理してみると、こうなる。

(<*|*>) :: (Monad m) => (a -> c) -> (a -> m b) -> (a -> m (c, b))
g <*|*> f = uncurry (=<<) . ((return.).(,).g &&& f)

{--

*FSDB> :t (id <*|*> getModificationTime)
(id <*|*> getModificationTime) :: FilePath -> IO (FilePath, ClockTime)

*FSDB> (id <*|*> getModificationTime) "/home/cut-sea/data/mbdb/data/%78"
("/home/cut-sea/data/mbdb/data/%78",Sat Jun 14 19:24:59 JST 2008)

*FSDB> mapM (id <*|*> getModificationTime) ["/home/cut-sea/data/mbdb/data/%78"]
[("/home/cut-sea/data/mbdb/data/%78",Sat Jun 14 19:24:59 JST 2008)]

*FSDB> mapM (id <*|*> getModificationTime) ["/home/cut-sea/data/mbdb/data/%78","/home/cut-sea/data/mbdb/data/%31%32%33"]
[("/home/cut-sea/data/mbdb/data/%78",Sat Jun 14 19:24:59 JST 2008),("/home/cut-sea/data/mbdb/data/%31%32%33",Wed Jun 18 00:03:04 JST 2008)]
--}

もう一つ欲しくなってきたな。cut-sea:2008/06/19 10:03:38 JST


GHC 6.8.3

リリースされたのかな。
まだリリースノート見てないけど、 今回からNetBSDも正式対応されているらしいとの噂を聞いてるので、 ビルドしてみようと思ふ。cut-sea:2008/06/18 08:42:54 JST


split

unfoldrを使ってみた。

split p xs = unfoldr (break' p) xs
    where
    break' :: (a -> Bool) -> [a] -> Maybe ([a],[a])
    break' p [] = Nothing
    break' p xs = case break p xs of
                  (_,[])    -> Just (xs,[])
                  (ys,_:zs) -> Just (ys,zs)

しかし、こういう分割系の関数で、ことごとくseparatorが残り要素に含まれてて スゲー不便だと思うのはオレだけか?

  • セパレータを使うならそうだけど。セパレータがない場合もいくらでもあるよね。たとえば、CamelCaseで書かれている空白のない文字列を単語に分解するとか。(2008/06/16 09:10:00 JST)

残りデータが同じものを返してたら、 自分で頭ハネることになって二度手間だと思うんだが。

*FSDB> split (=='/') "home/cut-sea/data/mbdb"
["home","cut-sea","data","mbdb"]
*FSDB> split (=='/') "/home/cut-sea/data/mbdb"
["","home","cut-sea","data","mbdb"]

どうもunfoldrはまだ良く分らん。cut-sea:2008/06/16 07:51:04 JST


Haskellのプログラムにおける設定ファイルとか

Scheme?とかだとS式で設定ファイルを書いておいて、readしちゃうってのは良くやる。

data Database = Database {prefix::String, encoding::String} deriving (Show,Read)

dbParse :: String -> Database
dbParse s = (read s)::Database

dbTest :: String -> IO ()
dbTest = putStrLn . prefix . dbParse

プログラムはこんなのをロードしてみる。 でもって、

Database {prefix="/home/cut-sea/data/hogeDB", encoding="UTF-8"}

Config.hsには上記のDBの設定を書いておく。 ghcの対話環境で、

*Main> let f = readFile "Config.hs"
*Main> f >>= dbTest
/home/cut-sea/data/hogeDB
*Main> 

readとshowがread/write不変なので簡単と思ってて、まぁ上記の通りすれば、 ちゃんと設定ファイルからDatabaseのデータをコンストラクトできるんだけど、 設定ファイルにコメントを書いたりするともうダメポ。

  • コメントを読み飛ばすreaderを自前で書くしかないよねきっと。 単純な構造の設定なら簡単だとおもうよ。–nobsun(2008/06/14 23:38:12 JST)
  • うーん。コメントが何行入るか分らない場合だと、(read s)::Typeのsにどこまで切り出すか分らないですよね?
    • 一度にやろうとせずに、コメントを全部とりのぞいてから、read するようにすればいいだけじゃない?(2008/06/16 09:14:06 JST)
    • コメント対応read。ちょいださだけどProgramming_玉手箱_文字列(2008/06/17 15:50:22 JST)

例えばConfig.hsにdbの設定だけじゃなくてサイトの名称やらURIのベースなんかを書く場合とかでは、 getContentsで済ませられないからreadをoverrideするだけじゃなくて設定ファイルパーサを書かなきゃならないのか。 一番良いのはHaskellのコードで設定を書いておいて、ファイルのロードだけで済ませられれば良かったんだけど、 考えてみれば、それが出来るとしたら対話型インタプリタ上で定義が出来るようなものだから無理っちゃ無理なのか。cut-sea:2008/06/15 07:09:51 JST

式を一つ読むっていうreadはできないのかしらん。 まじめにパースしなきゃならんなんてこたないと思うのね。cut-sea:2008/06/14 09:05:12 JST

もしかしたら

myDatabase = Database {prefix="/home/cut-sea/data/hogeDB", encoding="UTF-8"}

としてメインプログラムでimportしるってのが常套手段なのか? なんかそれで良さげな気がしてきたな。

  • ダメじゃん。コンパイルされたら一緒に巻き込まれちゃう。コンフィグファイルの意味なしだよ。それじゃ。 なんか小回り効かないな。皆どうしてるんだろう?cut-sea:2008/06/14 22:03:16 JST

ただし、その設定ファイルに色んな項目を入れたらimportとかモジュール宣言が ずらずら並んでおよそ設定ファイルらしくなくなるけど。。。cut-sea:2008/06/14 09:09:50 JST

Cabalのpackage.confは人手で作成するものじゃないからかも知らんが、 リストにしてつらつら並べてるな。ただしコメントはない。
やっぱこれは設定ファイルってのとは違うよなぁ。 システムの管理ファイルだから成立してるんだ。cut-sea:2008/06/14 09:18:53 JST


日本語文字

正直じゃまくさい。
ここが細かいことを気にしなくても扱えるとスゲーうれしいんだけど。
どう書く.orgのnobsunの解を見ながらようやく使えそうな気配。

{-*******************************************************
       {- encoding: utf-8; mode: haskell -}
              File System DataBase
*******************************************************-}

module Main where

import Control.Applicative
import Network.URI
import Codec.Binary.UTF8.String
import qualified System.IO.UTF8 as U

escape = escapeURIString (const False) . encodeString

unescape = decodeString . unEscapeString

sample0 = "~url quote../ほげ"
sample1 = "!(foo*)"

main = mapM_ (U.putStrLn . ((++) . unescape <*> (" <- "++)) . escape) [sample0,sample1]

これで

*Main> main
~url quote../ほげ <- %7E%75%72%6C%20%71%75%6F%74%65%2E%2E%2F%E3%81%BB%E3%81%92
!(foo*) <- %21%28%66%6F%6F%2A%29
*Main> 

エスケープしてファイル名にして問題無いものに無害化されているのと、 それをアンエスケープして元に戻せることを確認。

ちなみにEmacs上でやるときはset-buffer-file-system-encodingをutf-8に。
terminal上でもUTF-8に移行しておいて良かったーってことなのかどうか知らんけど 問題なさげ。

[email protected]> runghc Test.hs
~url quote../ほげ <- %7E%75%72%6C%20%71%75%6F%74%65%2E%2E%2F%E3%81%BB%E3%81%92
!(foo*) <- %21%28%66%6F%6F%2A%29
[email protected]> 

fsdbを実装しようと思ったんだけど、とりあえずこれを使えば行けそうかな。cut-sea:2008/06/13 05:26:19 JST


モナド

なんとなく分ってきた。 ので、もう少し頭の中が整理できたら何か書くかも。cut-sea:2008/06/11 09:55:31 JST


XHtml

ライブラリは薄いラッパーを作成中。

*Main> h1 "H1"
<h1
>H1</h1
>
*Main> h1 $ toHtml "Normal Header"
<h1
>Normal Header</h1
>
*Main> bold "BOLD"
<b
>BOLD</b
>

となる。ちょっとだけ楽チンになる予定だろうか(自信なし)。

一旦了。

module Main where

import HtmlLite
import CGI

myLink = anchor![(identifier "toGoogle"),(name "toGoogle"),(href "http://www.google.co.jp")]$"click here"
main = cgiMain $ const $ putHtml myLink

で、

*Main> main
q: 
Content-type: text/html; charset="UTF-8"

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><a id="toGoogle" name="toGoogle" href="http://www.google.co.jp"
  >click here</a
  ></html
>

はじめて、「あ、K!」と思えた。
というのはどーでもいくて、XHTML的にはbodyタグ必須かー。どーしよ。 便利なutilityとしてbody内部だけ書けばいいやつを追加すっかぁ? いやいや、pageみたいなのを作るか。cut-sea


UTF8

入力出力はUTF-8のライブラリでwrapしないと、うれしさ半分だな。 もっかい使い方を調べてみるか。

って書いたけど、nobsun_utf8にあんじゃん。
ただし今のバージョンだとライブラリパスが少し変ったらしいので、

import System.IO.UTF8   -- ここ変更になった
import Prelude hiding (getContents,getLine,readFile
                      ,putStr,putStrLn,writeFile)
import System.IO hiding (getContents,getLine,readFile
                        ,putStr,putStrLn,writeFile
                        ,hGetContents,hGetLine

ですね。

*Main> main
お名前をどうぞ.
伊東 勝利
ようこそ,伊東 勝利さん.

おお、いけたいけた。
じゃあ例のCGIだとどうか

*Main> main
q: who=%E4%BC%8A%E6%9D%B1%20%E5%8B%9D%E5%88%A9
Content-type: text/html; charset="UTF-8"

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><h3
  >&#12371;&#12435;&#12395;&#12385;&#12431; 伊東 勝利 &#12373;&#12435;</h3
  ></html
>

あら、文字化けと思ってたけど、良く見たら実体参照だったのか。
クライアントからPOSTされた文字列はちゃんと自分でエスケープしてから 送らないといけなかったんだっけ。

*Main> stringToHtml "伊東 勝利"
&#20234;&#26481; &#21213;&#21033;
*Main> stringToHtml "<&>"
&lt;&amp;&gt;

stringToHtmlは勝手にescapeしてんね。

Convert a String to Html, converting characters that need to be escaped to HTML entities.

うーん。完全にフレームワークに頼ってて基本を忘れてんなぁ。cut-sea:2008/06/10 02:59:39 JST

q: who=%3C%E4%BC%8A%E6%9D%B1+%E5%8B%9D%E5%88%A9%3E
Content-type: text/html; charset="UTF-8"

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><h3
  >&#12371;&#12435;&#12395;&#12385;&#12431; &lt;伊東 勝利&gt; &#12373;&#12435;</h3
  ></html
>

あ、ちゃんとエスケープされてる。

gosh> (use text.html-lite)
#<undef>
gosh> (html-escape-string "伊東 勝利")
"伊東 勝利"
gosh> (html-escape-string "<>")
"&lt;&gt;"
gosh> (html-escape-string "こんにちわ")
"こんにちわ"

あ、そっか別にエスケープって日本語は関係ないんだっけ。
単にタグなんかになってしまう文字をエスケープするだけだ。 属性値とかでURLになってしまうようなのは別途uriencodeは必要だけど。

ってことは問題はなんでリテラルで埋め込んだ日本語だけが実体参照にしちゃうのかだけど。cut-sea:2008/06/10 03:46:10 JST


unfoldr

unfoldrが少し分りかけてきた。

lines' s = unfoldr f s
    where
    f b = if (b=="") then Nothing else Just $ break' (=='\n') b

breakでpredicateがTrueになったとこまで含めるバージョンってないすか?
spanもbreakも

*Main> break (=='\n') "Hello,\nWorld"
("Hello,","\nWorld")
*Main> span (/='\n') "Hello,\nWorld"
("Hello,","\nWorld")

って改行コードが後続に含まれてしまふのでつ。仕方なくbreak’を実装。

break' p xs = break'' [] xs
    where
    break'' res []     = (res, [])
    break'' res (x:xs) = if p x then (res++[x],xs) else break'' (res++[x]) xs

これで

*Main> break' (=='\n') "Hello,\nWorld"
("Hello,\n","World")

ようやくlines’の実装にunfoldrに使えた。 unfoldrの感覚が掴みたかっただけだけど、えらい遠回りしてしまった。

  • あら、linesは改行を含まないのかー。いーけどこんな風にすればおけー。

    split p xs = break'' [] xs
        where
        break'' res []     = (res, [])
        break'' res (x:xs) = if p x then (res,xs) else break'' (res++[x]) xs

しかし我ながら話があっちゃこっちゃと…分裂気味かも。cut-sea:2008/06/10 01:53:15 JST

あ、そうそう。なんで、unfoldに話が飛んだかというと、

  1. 下の件でパーサ書かなくていーんだよなーって思ったけど、
  2. どの段階でread c::Typeってするんだ?と思い、
  3. 多分1行に1式とかってやるんだろうなぁと思って、
  4. linesかーと思って、
  5. String->[String]ってことはunfoldrで書けるんだよなーと思った

のでした。
よかった脈絡あった。cut-sea:2008/06/10 02:04:01 JST


read

Cabalのpackage.confを見てて、そういやreadは?と思ったので確認。

data Color = Red | Blue | White | RGB (Int,Int,Int) deriving (Show, Read)

ってな具合にRead classのinstanceにしておいて

*Main> read "RGB (0x7f,0x8e,0x4e)" :: Color
RGB (127,142,78)

ってことでいいのね。
良かった。 設定ファイルのためのパーサを書かなくていいんだよね。

*Main> let s = (show "hoge")
*Main> s
"\"hoge\""
*Main> read s::String
"hoge"
*Main> let c = show $ RGB (0x80,0x8c,0x8c)
*Main> c
"RGB (128,140,140)"
*Main> read c::Color
RGB (128,140,140)

うむ。read/showでread/write不変ってことで再確認。cut-sea:2008/06/10 00:55:20 JST


,はなんだ?

Prelude> :t (,)
(,) :: a -> b -> (a, b)

についてです。どーんと大盤振舞い。(w

Prelude> :t (,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,) :: a
                                                                   -> b
                                                                   -> c
                                                                   -> d
                                                                   -> e
                                                                   -> f
                                                                   -> g
                                                                   -> h
                                                                   -> i
                                                                   -> j
                                                                   -> k
                                                                   -> l
                                                                   -> m
                                                                   -> n
                                                                   -> o
                                                                   -> p
                                                                   -> q
                                                                   -> r
                                                                   -> s
                                                                   -> t
                                                                   -> u
                                                                   -> v
                                                                   -> w
                                                                   -> x
                                                                   -> y
                                                                   -> z
                                                                   -> t28
                                                                   -> t29
                                                                   -> t30
                                                                   -> t31
                                                                   -> t32
                                                                   -> t33
                                                                   -> t34
                                                                   -> t35
                                                                   -> t36
                                                                   -> t37
                                                                   -> t38
                                                                   -> t39
                                                                   -> t40
                                                                   -> t41
                                                                   -> t42
                                                                   -> t43
                                                                   -> t44
                                                                   -> t45
                                                                   -> t46
                                                                   -> t47
                                                                   -> t48
                                                                   -> t49
                                                                   -> t50
                                                                   -> t51
                                                                   -> t52
                                                                   -> t53
                                                                   -> t54
                                                                   -> t55
                                                                   -> t56
                                                                   -> t57
                                                                   -> t58
                                                                   -> t59
                                                                   -> t60
                                                                   -> t61
                                                                   -> t62
                                                                   -> t63
                                                                   -> (a,
                                                                       b,
                                                                       c,
                                                                       d,
                                                                       e,
                                                                       f,
                                                                       g,
                                                                       h,
                                                                       i,
                                                                       j,
                                                                       k,
                                                                       l,
                                                                       m,
                                                                       n,
                                                                       o,
                                                                       p,
                                                                       q,
                                                                       r,
                                                                       s,
                                                                       t,
                                                                       u,
                                                                       v,
                                                                       w,
                                                                       x,
                                                                       y,
                                                                       z,
                                                                       t28,
                                                                       t29,
                                                                       t30,
                                                                       t31,
                                                                       t32,
                                                                       t33,
                                                                       t34,
                                                                       t35,
                                                                       t36,
                                                                       t37,
                                                                       t38,
                                                                       t39,
                                                                       t40,
                                                                       t41,
                                                                       t42,
                                                                       t43,
                                                                       t44,
                                                                       t45,
                                                                       t46,
                                                                       t47,
                                                                       t48,
                                                                       t49,
                                                                       t50,
                                                                       t51,
                                                                       t52,
                                                                       t53,
                                                                       t54,
                                                                       t55,
                                                                       t56,
                                                                       t57,
                                                                       t58,
                                                                       t59,
                                                                       t60,
                                                                       t61,
                                                                       t62,
                                                                       t63)
Prelude> 

型がppされたー。ってツッコむトコ違うっ!

はてさて,はなんだろう? ,,を適用して合成したわけじゃないよねぇ。

ちなみにどんだけヒマなんだと思いつつ

:t (,,,,,,,,,,,, .. ,,,,,,,,,,,)
               :
              t8964,
               :
              t8965,
               :
              t8966)
Prelude> 

あたりまで確認。 一回に時間がかかるのでヤメた。 なにやらppもあやふやだし。

Prelude> :t (,)
(,) :: a -> b -> (a, b)
Prelude> :t (,,)
(,,) :: a -> b -> c -> (a, b, c)

を見比べながら…

Prelude> :t (,)(,)
(,)(,) :: b1 -> (a -> b -> (a, b), b1)
Prelude> :t (,).(,)
(,).(,) :: a -> b -> (b1 -> (a, b1), b)

うーん。(,)だけから合成して(,,)を作れるわけでもないか。 そもそも構造が別だから出来上ったものは(,)だけの組み合わせだし。
で、,って何?cut-sea:2008/06/09 23:44:47 JST


make

また捜してしまったのでメモ。

 ghc -o app.cgi -optl-static --make App.hs

Cabal

CabalによるHaskellのパ(ハ?)ッケージインストールなんだが、

runghc Setup.hs configure
runghc Setup.hs build
runghc Setup.hs install

とすると/usr/local/の下に入る。それはまぁいいとして、 そのディレクトリが…

/usr/local/lib/<pkg-dir>/ghc-6.8.2/...

みたいに散らかしやがるのに今気付いた。サイテー!! ghc-6.8.2の下にまとめてくれよ、と。
冗談ぽく書いてるけど結構ムッときた。 どうにも他のアプリで共有できるものにも見えないしなぁ。 もしかしてpkgsrcでbuildしたもの(ghc-pkg)がおかしいのかしら。 それとも元々そーゆーもの?

  • とりあえず/usr/localってのはdefaultでそういうものなのは普通で、 NetBSDのpackageが例外だと思うので、–prefix=/usr/pkgで指定は良し。 問題は…

    –libsubdir=dir

        A subdirectory of libdir in which libraries are actually installed.
     For example, in the simple build system on Unix, the default libdir is /usr/local/lib,
     and libsubdir contains the package identifier and compiler,
     e.g. mypkg-0.2/ghc-6.4, so libraries would be installed in /usr/local/lib/mypkg-0.2/ghc-6.4. 

    ぐぇぇ。cut-sea:2008/06/09 23:18:24 JST

というわけで手で移動させたらimportできなくなった。(当然)
/usr/pkg/lib/ghc-6.8.2/packages.confにいかにもHaskellのコードっぽく パッケージのメタデータが格納されていらっしゃるので、このパスを修正。 そしたらimportできるようになったらしい。ふう。 しかし、parsecが2.1.0.0があるのに3.0を入れてしまったんだけど、 どっちが見えることになるんだろう。

[email protected]> ghc-pkg describe parsec
name: parsec
version: 2.1.0.0
      :

ってことは2.1.0.0なんだろうか? それとも

[email protected]> ghc-pkg latest parsec
parsec-3.0.0

で3.0.0が使われるのか。 依存関係があるならイモヅルで引っ張られるのかもしれないが、 自分がimportした時はどうなるのやら。

  • あーでもHaskell本体とは別の話だよなぁ。イモヅルはないか。 ってことは、どっちが見えるんだろう。

他のパッケージのメタデータの中にも2.1.0.0の参照っぽいのが入ってるので、 気軽に削るのもマズそうだ。 メタデータ見てたらhaddockとかいうのパスには実際には何もなさげなんだけど、いーんだろうか?
つか、今頃気付いたけど、 ガイドちゃんと読めってことですね。

とりあえず6.8.3からNetBSDがサポされるらしいので、 その時には自分でbuildしようとは思ってたりする。 と、ゆーわけで早く出してたもれ。

あ、そうだ。 えらい寄り道したけど、元々DB関係のライブラリをあさってたんだった。
buildに失敗(BerkeleyDB-0.3)したり、依存するライブラリ(その他もろもろ)が多かったり、 なんか似たようなのがイロイロあるみたいに見えるんだけど、 標準でコレいっとけみたいなのはドイツだ?cut-sea:2008/06/09 22:18:57 JST


CGI

ライブラリを自作した。
Gauche?のCGIライブラリみたいに、 開発中とかでREQUEST_METHODが無い場合は 標準入力からQUERY_STRINGを受け取れるようにした。 これがやっぱり便利。

-- まずはお試し
*Main> cgiMain test2
q: 
Content-type: text/html; charset="UTF-8"

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><h3
  >&#12371;&#12435;&#12395;&#12385;&#12431;&#12289;&#12354;&#12435;&#12383;&#35504;?</h3
  ></html
>

-- クエリとしてwho=cut-seaを与えてみる
*Main> cgiMain test2
q: who=cut-sea       -- ← ここでqueryを与える
Content-type: text/html; charset="UTF-8"

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><h3
  >&#12371;&#12435;&#12395;&#12385;&#12431; cut-sea &#12373;&#12435;</h3
  ></html
>
*Main> 

入力出力はUTF-8のライブラリでwrapしないと、うれしさ半分だな。 もっかい使い方を調べてみるか。
以下がサンプル。 ようやくCGIが書ける準備がでけた。のか?

text/plainのサンプル

{- printenv -}
test1 req = getEnvironment >>=  putText . concat . map (\(k,v)->k++" = "++v++"\n") . sort

main = cgiMain test1

text/htmlのサンプル

{- hello -}
test2 req = case getparam "who" req of
                 Just w   -> putHtml $ h3 $ toHtml $ "こんにちわ " ++ w ++ " さん"
                 Nothing  -> putHtml $ h3 $ toHtml "こんにちわ、あんた誰?"
main = cgiMain test1

toHtmlが邪魔くせえ。 でもinstance HTML Stringしようとしたけどムリくさかったしなぁ。cut-sea:2008/06/09 08:32:06 JST

  • ? Text.XHtmlモジュール使ってるんなら,String は HTML クラスのインスタンスだよ.すでに.だから,toHtmlメソッドが使えるんだと思うけど...nobsun
  • あーそっか。勘違い。そうですね。
    私がやりたかったのはh1 “ほげ”としたかったので、 Htmlとして文字列をそのまま使いたかったのでした。
    が、コンストラクタか…。 うーん。避けるとしたらh1とかとは別にtH1みたいなのを用意するしかないのかな。cut-sea:2008/06/09 14:03:15 JST

printenv with (<*>)

Sコンビネータも使ってみる。

#!/usr/bin/env runghc

import System.Environment
import Data.List
import Control.Applicative

main = putStrLn "Content-type: text/plain\n\n" >> getEnvironment >>= mapM_ (putStrLn . ((++) . fst <*> (" = "++) . snd)) . sort

逆に長くなってるやんけ、と言われそうだけど、いーんです。

*Main Control.Applicative> ((*).fst<*>snd) (2,3)
6
*Main Control.Applicative> (zip<*>tail) [1..10]
[(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)]
*Main Control.Applicative> (zip<*>drop 3) [1..10]
[(1,4),(2,5),(3,6),(4,7),(5,8),(6,9),(7,10)]

ふーん。じゃあmyLast。

myLast n = (over <*> drop n)
           where
           over x [] = x
           over (x:xs) (y:ys) = over xs ys

なるほど、Sコンビネータってそういうことか。 とナットク。cut-sea:2008/06/07 16:06:45 JST

  • over はfoldl をつかえばかっこいいかも
over = foldl (const . tail)

こうか。

myLast n = (foldl (const . tail) <*> drop n)

なるほどね。 まだ K は全然イメージが出来てないな。cut-sea:2008/06/08 01:50:51 JST

  • こう :)

    lastn = (foldl (const . tail) <*>) . drop
  • うーん。 Sを片側にしちゃうとこれまたイメージが湧かない。cut-sea:2008/06/08 02:32:45 JST


printenv with (>>=)

もちっとらしく書いてみる。

#!/usr/bin/env runghc

import System.Environment
import Data.List

main = putStrLn "Content-type: text/plain\n\n" >> getEnvironment >>= mapM_ (\(k,v)->putStrLn$k++" = "++v) . sort

というわけでほぼワンライナー。cut-sea:2008/06/07 12:56:24 JST

  • らしく,ワンライナーにこだわるなら

    main=putStr.unlines.(unlines["Content-Type: text/plain"]:).map(uncurry((.(" = "++)).(++))).sort=<<getEnvironment
  • どう思考したらこう書けるようになります?cut-sea:2008/06/08 02:56:52 JST


printenv

一番簡単なところから始めてみる。

#!/usr/bin/env runghc

import System.Environment
import Data.List

main = do es <- getEnvironment
          putStrLn "Content-type: text/plain\n\n"
          mapM_ putEnv $ sort es
    where
    putEnv (k,v) = putStrLn $ k ++ " = " ++ v
  • System.EnvironmentはgetEnvironmentのため
  • Data.Listはsortのため

  • この課題の場合,プログラムと外界とのインタラクションは1つです.すなわち,環境変数を外界から入力するのと,その一覧を出力するという1回だけですよね.それなら,出力は一箇所にまとめて 出力 <- 加工 <- 入力 のようにしたほうが読みやすくなると思いますよ.nobsun

    main = putStr . unlines . addHeader . map showEnv . sort =<< getEnvironment
    showEnv (k,v) = k ++ " = " ++ v
    addHeader = (unlines ["Content-Type: text/plain"] :)

cgiとして動かそうとするとerrorになる。 httpd/error_logにはrunghc: cannot find ghcとあるから起動時に runghcってのはどうもghcをcallしてるだけなんでしょう。 apacheから見つけらんないのかな。

% runghc --help
runghc: syntax: runghc [-f GHC-PATH | --] [GHC-ARGS] [--] FILE ARG...

で、-f /usr/pkg/bin/ghc って与えるものらしいのだけど、ダメっぽい。

% ghc printenv.hs -o penv

としてバイナリをアップしておけば問題なく動作する。 最後は勿論それで良いんだけど、開発中はコンパイル面倒だな。 解決法は知らねぇ。cut-sea:2008/06/07 12:39:26 JST


まずはEchoサーバから

nobsunに注文したら書いてくれたので、さっそく試してみた。 が、動かないよぉ。

悲しいのでNetwork.Socketを使って実装してみた。cut-sea:2008/06/05 05:30:42 JST

こっちね -> Programming_Network


再入門

しました。 今度は目標が見えているので最後まで頑張る。cut-sea:2008/05/24 11:00:00 JST



Last modified : 2011/10/06 07:23:11 JST