はてな使ったら負けだと思っている deriving Haskell このページをアンテナに追加 RSSフィード

2010-01-25

[][] THへのふまん 01:01  THへのふまん - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  THへのふまん - はてな使ったら負けだと思っている deriving Haskell

こう書けるべき!

[d| 'name = 'rnd |]

まぁはすけるは(- 1)が函数にならない時点で終焉です。時代は受験です受験!!

まくぶくしばらくしまおうか……

[][] 打倒C++ 00:30  打倒C++ - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  打倒C++ - はてな使ったら負けだと思っている deriving Haskell

それ Template Haskell でできるよ!

PerlとC++にしかできないような気がするアノ機能(挑戦者募集中) - 簡潔で覚えやすいタイトルを3秒で思いつく程度の能力

とはいいつつ若干(かなり)ルール無視なかんじで、まあとりあえず代入する変数(束縛する函数)をランダムで決めればいいよね……と解釈しました。

ルール厳守版だと、 id:eagletmt さんが TwitterでIORefで書いてるのがすでにありますし、なによりぼくはむかし IORefとunsafePerformIOくんには痛いメに遭っているのできらいです。eagletmtさんはunsafeつかってないけど、とにかくきらいです。

そこで、1/2の確率でaまたはbをランダムに定義するプログラムを書いてみました。ルールからは外れますが気にしてはいけません。


RandomDef.hs:

{-# LANGUAGE TemplateHaskell #-}
module RandomDef where
import Language.Haskell.TH
import System.Random

$( do rnd <- runIO $ randomIO
      let nm = mkName (["a", "b"] !! (rnd `mod` 2))
      sequence [runQ $ valD (varP nm) (normalB [| rnd |]) []]
  )

Main.hs:

import RandomDef
main = print a

Template Haskell の制約上、なんか二つに分けなきゃいけないのが不満ですが、こんなかんじです。

実行すると1/2の確率で勿論失敗します。


実行:

$ runhaskell Main.hs
515889430

$ runhaskell Main.hs
586851382

$ runhaskell Main.hs

Main.hs:2:13: Not in scope: `a'

$ runhaskell Main.hs
341328210

$ runhaskell Main.hs

Main.hs:2:13: Not in scope: `a'

$ runhaskell Main.hs
697186460

$ runhaskell Main.hs

Main.hs:2:13: Not in scope: `a'

$ runhaskell Main.hs
2076927286

けなげですね。すばらしい。シュレーディンガーのはすけるですね!意味がわかりません。

まあ、日頃おカタい静的なヤツだと思われてるはすけるちゃんも、実は意外と動的なところあるじゃん……みたいな認識が日本に広まれば、僕はきっと受験に合格すると思います。しなかったらはすけるちゃんの所為です。はすけるちゃん、立派な大人になってください。美味しゅうございました。

トラックバック - http://haskell.g.hatena.ne.jp/mr_konn/20100125

2009-09-13宇宙HOC月間

[][][] HOCでTwitterクライアント書いてみた  HOCでTwitterクライアント書いてみた - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  HOCでTwitterクライアント書いてみた - はてな使ったら負けだと思っている deriving Haskell

勉強をサボってHOC (Haskell Objective-C Binding)Twitterクライアントを書いてみました。

アプリとソース一式はこちら

あとgithubにもソースあります


一通りタイムラインの定期確認と発言は出来る様になってます。

あとIntelならGHCがインストールされてない普通の環境でも動くと思います。

PPCの人はお手数ですが自力でビルドしてください。

$ runhaskell Setup.hs configure
$ runhaskell Setup.hs build

とでもやれば出来ると思います。人によっては --user が必要かも。

コンパイルする場合は HaXml と HTTP と HOCと utf8-stringがrequiredなのでいれといてください。バイナリになっちゃえば関係ないです。

感想としてはまあ、クラス弄るなりなんなりするところは、ここまで来るとObj-Cでやった方が早い。意味わかんないinstance宣言しないといけないし。

ただまあHaskellのスレッド機構を使えたり、XMLパースにHaXmlつかったりできるところはやはり強みだと思う。そうそう、今回はHaXmlお試しの意味も込めてました。簡単に書けすぎてお試しにならなかったかもしれませんが、とにかくHaXmlは簡単でした。

余談だけど HaXml と Haml は名前がややこしい。Hamlは亡びるべきですね!!

全体的に見ればなかなか快適な開発だったと思います。あとはIBが自動的にソース読んで、Outlet と Action を自動設定してくれるといいんですが、まあ希望です。

RubyCocoaの方がまあObj-Cとはパラダイム的にもマッチしているので、やっぱりあの快適さには敵いません。

その内Haskell Ruby Obj-C ハイブリッド(?)なアプリとか書くのもいいかもしれませんね。


HOCプログラミングの方法論はまあコード読んで貰えればわかると思いますが、そのうち詳しく書いてみようと思います。

動作画面は以下。

f:id:mr_konn:20090913144748j:image

f:id:mr_konn:20090913144747j:image


アイコンはHaskellリスペクトな感じでこんなんです。

f:id:mr_konn:20090913145147j:image

トラックバック - http://haskell.g.hatena.ne.jp/mr_konn/20090913

2009-09-06そろそろ勉強に戻るべきらしい

[][] DPH まとめ  DPH まとめ - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  DPH まとめ - はてな使ったら負けだと思っている deriving Haskell

(2009/09/07 一部編集・追記)

昨日から今朝に掛けて、@shelarcy さんに 助けられながら Data Parallel Haskell (dph; データ並行 Haskell) と格闘した続報&まとめ。

あと、後で書いた『Unlifted と vectoriseの簡単な比較』の方にvectorise と Unliftedの比較を手短に纏めたので、そちらも見てください。

  • dph には並列実行型と逐次実行型がある
    • コンパイル時に渡すオプションが -fdph-par なら並列実行、 -fdph-seqなら逐次実行
    • dph-par も dph-seq も同じ構成のモジュールを提供していて、内部実装が違うので、パッケージ切り替えだけで対応出来る。
    • Data.Array.Parallel.Unlifted.Parallel と .Sequential がそれぞれのプリミティヴな実装(これは片方のパッケージにしかない)
  • dph を利用するには二つの方法
    1. Unlifted を利用
      • import Data.Array.Parallel.Unlifted して使う。
      • プリミティヴな実装。構文糖はなし。
      • 一部追い付いてないがわりと最適化されていて、早い。
      • 並列処理部分でなければ、普通のデータと混ぜて使える
      • Prelude のリスト操作と概ね同じ名前の函数を提供
        • 上述の通りUnlifted は Unlifted.Parallel、Unlifted.Sequential のラッパ
        • 基本的には .Parallel と .Sequential で提供されている函数のエイリアス
        • Unlifted だけ、乃至は .Parallel/Sequential のみしか提供していない函数もまだある
        • mapU の様に函数名が U で終わるのがSequential系、UP で終わるのがParallel系
      • cons とかないのでまだリストの様には扱えない(一要素配列を作ってから +:+ で繋げる;一要素配列つくるのもreplicateをつかわなきゃだめっぽい?)
      • なぜかParallel系はproductをexportしていない(実装はある/sumはexportしてるのに……?)
      • Unlifted.map の中で更にParallel系処理を使おうとするとデッドロック(?)する。
        • 多分二重三重の並列はまだ苦手?
        • 逐次実行(-fdph-seq)にすると実行は出来る。
      • Parallel と Sequential の使い分け(2009/09/07 1:18 追記)
        • Unlifted.Parallel も .Unlifted も直交するものであって、共存させて使うのが正しい。
          • そもそも、.Parallel で使われてる配列は .Sequentialで定義されている
        • 上述 cons や一要素配列はそもそも並列化出来ない作業で、そう云った操作は .Sequential でしか提供してないし、当然。
        • 混ぜるな危険な作業や、効率性はこちらの側で判断して使い分ける
          • 上述 .Parallel.mapUP 内で更にParallelな作業をすると DEAD とか
          • sequentialの方が速いとかリストの方が速いとか
          • vectoriseが賢くなる未来を夢見よう!
    2. vectorise (ベクトル化)を利用
      • 現状 Unlifted系のための構文糖を提供(GHC.PArr から Unlifted への変換)
        • [: a | a <- as, odd a :] の様にリストみたいな表記で扱える
      • Prelude を置き換える Data.Array.Parallel.Prelude を import して使う
      • 超まだまだ発展途上
        • ベクトル化出来るデータと出来ないデータの共存が出来ない
          • 並列処理部分と利用部分を分けて記述する必要あり
          • f . g . h とか f $ g x みたいなPrelude提供の演算子・函数は使うと怒られる(並列化出来てない)
          • パターンマッチが使えない
        • 型クラスがない
          • Intの処理とDoubleの処理とで別のPreludeをimportする必要がある(Data.Array.Parallel.Prelude.Int/.Double)
        • GHC.PArr 提供の函数全てに対応している訳ではない
          • dropP などは未対応なので使えません!
        • 取り敢えずな実装が多いので、速度は度外視(並列化なのに……)
          • 現状ではリストかunlifted系で実装した方が早い
    3. 両者に共通なこと
      • まだ一部の型しか並列化出来ない
        • Int, Word8, Double など。Integer とか Char とか List とかを配列の要素にすることが出来ない。
      • 並列化が入り組んでるところは上手く処理出来ない?
      • 未来の実装だと速い!

参考文献

[][] Unlifted と vectoriseの簡単な比較  Unlifted と vectoriseの簡単な比較 - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  Unlifted と vectoriseの簡単な比較 - はてな使ったら負けだと思っている deriving Haskell

  • dph の本丸は vectorise。[: a | a <- as, isAlpha a :] みたいな感じで並列配列を扱える。
  • 現状では vectorise は Unlifted 配列の構文糖と同義
    • 本体はUnliftedで、GHC.PArr をUnliftedに変換するのがvectoriseの役割
  • 両方とも並列に対応してないデータ(Char とか Integer とか [a]とか)がある。
  • Unlifted と vectorise の 比較
    1. 速さ
      • 現状ではUnliftedに軍配
        • vectorise は取り敢えずな実装が多いのでかなり遅い
      • 将来的にはvectoriserはもっと速くなるよてい(6.11の時点で現在よりもある程度速いらしい)
    2. 可読性&書き易さ
      • リストの様に書ける点では vectoriser が楽チン
        • Unlifted は 全部函数で書く。 consU x xs (cons) とか singleton x (一要素配列)とか。
        • ネストした配列や木構造などを直接扱える
          • Unlifted ではちょっと大変
      • 並列化未対応データPrelude函数と共存して書ける点では Unlifted がラク
        • vectoriseは型クラスに対応していないのでData.Array.Parallel.Preludeで全部再定義している
          • ($)もねえ! (.) もねえ!showして確認もできねえ!
          • 少しでも並列化未対応なデータがモジュール内に登場すると怒られる
    3. 将来性
      • vectoriseはこれから頭が良くなるので、移植性を考えたらvectoriseです
      • Unliftedは内部実装なので使用が変わったり、最悪廃止になるかもしれない(飽くまでかもしれない)
        • 現状の速さ優先だったらUnliftedを使って、SequentialとParallelを頑張って使い分けるのが良いでしょう

参考文献

DPHまとめ』に同じ。

[][][] 上記を踏まえた皇帝の新しいProblem 254 02:14  上記を踏まえた皇帝の新しいProblem 254 - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  上記を踏まえた皇帝の新しいProblem 254 - はてな使ったら負けだと思っている deriving Haskell

Unlifted最新版。

{-# LANGUAGE PArr, PackageImports #-}
{-# OPTIONS_GHC -Odph #-}
module Main where
import qualified "dph-prim-par" Data.Array.Parallel.Unlifted as U
import qualified "dph-prim-seq" Data.Array.Parallel.Unlifted.Sequential as S
import System.Environment
import Data.Char

toDigits :: Int -> U.Array Int
toDigits = U.fromList . map digitToInt . show

forEachDigit fn = U.sum . U.map fn . toDigits
forEachDigit' fn = U.map fn . toDigits
f = forEachDigit (S.productU . S.enumFromToU 1)
sf = forEachDigit id . f
g i = sub 1
  where sub n = if sf n == i then n else sub (n+1)
sg = forEachDigit id . g

sumsg x = S.sumU . S.mapU sg . S.enumFromToU x

main = do args <- getArgs
          case args of
            (x:y:_) -> print $ sumsg (read x) (read y)
            (x:[])  -> print $ f $ read x
            _       -> putStrLn "use just as you like"

$ ghc -Odph -fdph-par -fdph-seq unl.hs --make -threaded
$ time ./unl 1 30  
291
./unl 1 30  1.13s user 0.63s system 89% cpu 1.976 total
$ time ./plain 1 30
291
./plain 1 30  0.01s user 0.00s system 95% cpu 0.018 total

ここから先の最適化は各自でがんばりましょう。

[][] HOCがインストール出来る様になったみたいです  HOCがインストール出来る様になったみたいです - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  HOCがインストール出来る様になったみたいです - はてな使ったら負けだと思っている deriving Haskell

HOC (Haskell Objective-C binding) が普通にインストール出来る様になったみたいです。

$ svn co http://hoc.googlecode.com/svn/trunk/hoc
$ cd hoc

したら、あとはREADME.txtに書いてある通りに、

$ cabal configure
$ cabal build
$ cabal install
$ cd Bindings
$ sh make-bindings-macos.sh
$ cd ../Tools
$ cabal configure
$ cabal build
$ cabal install

とすれば普通にインストール出来ちゃいました(環境に依っては--user とか sudo とか必要かもしれません)。

Samples/以下のサンプルも普通にビルド出来ました。やったね!

受験が一段落したらHaskellCocoaアプリをつくりまくりたいと思います。

トラックバック - http://haskell.g.hatena.ne.jp/mr_konn/20090906

2009-09-05世界 dph デー

[][][] ProjectEuler を Unlifted で解いたらえらい速くなったけどまた躓いた  ProjectEuler を Unlifted で解いたらえらい速くなったけどまた躓いた - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  ProjectEuler を Unlifted で解いたらえらい速くなったけどまた躓いた - はてな使ったら負けだと思っている deriving Haskell

Data Parallel Haskell で格闘した結果歯医者に行くことになった経緯は前記事を参照のこと。

その後、歯医者から帰ってくると、Twitter@shelarcy さんから アドヴァイスを頂いてました。

@mr_konn 残念ながら、現在の DPH の vectoriser はあまり賢くありません。http://bit.ly/11iJoE -dph-seq を代わりに使って逐次版を作らせてみると、"Stack space overflow"になってしまうのが分かると思います。

http://twitter.com/shelarcy/status/3777048271

との事で、そうかぁ現行安定版ではまだ発展途上なんだなぁ……と。

@mr_konn 念のため手元にある ghc-6.11.20090803 を使って試してみました。こちらではちゃんと結果を返してくれますね。-fdph-par を使うとかなり遅くなりますが、-fdph-seq ならリスト版と比べてもそれほど遜色のない速度で答えを返してくれます。

http://twitter.com/shelarcy/status/3777587520

と云うことなので、未来の発展に期待です。

その後、shelarcyさんが勧めてくださった aggressively optimized for insanity - Data Parallel Haskell fun を読む。どうやら Data.Array.Parallel はラッパみたようなもので、まだまだ未実装な部分が多く、プリミティヴな実装である Data.Array.Parallel.Unlifted 充分早く動く、と云う風に理解する。

Unlifted は果してvectoriseするのかなぁどうなのかなぁなんて頓珍漢なことを考えていたら、

@mr_konn 現在の所、[:a:] に対する操作を Unlifted な配列に対する操作に変換するのが vectorise (flattening) の役割なので、Unlifted に対する vectorise はそもそも不要です。(将来的にはそうでなくなるかもしれませんが)

http://twitter.com/shelarcy/status/3778602025

との事で、それじゃあunlifted使ってみるべ、と。

Problem 10 で小手調べ

まずProblem 10 を軽く解いてみる。200万までの素数の総和を求める。ただし200万までやると時間かかるので、適当に10万まで。

リスト版(Plain10.hs):

module Main where

primes = sieve [2..100000]
sieve xs | null xs   = []
         | otherwise = 
  let (p:ps) = xs in
      p:sieve [q | q <- ps, q `mod` p /= 0 ]

sums = sum primes
main = print sums

Unlifted 版 (Unl10.hs):

{-# LANGUAGE PArr #-}
{-# OPTIONS_GHC -fdph-par #-}
module Main where
import qualified Data.Array.Parallel.Unlifted as U
import Data.Array.Parallel.Unlifted.Parallel

primes = sieve $ U.enumFromTo 2 100000
sieve xs | U.length xs == 0 = U.empty
         | otherwise = 
  let p = U.replicate 1 (xs U.!: 0)
      ps = dropUP 1 xs in p U.+:+ (sieve $ filterUP (\q->q `mod` (p U.!:0) /= 0) ps)

sums = U.sum primes
main = print sums

コンパイル:

$ ghc --make Plain10.hs
$ ghc -Odph -fdph-par Unl10.hs --make -threaded

計測結果:

$ time ./Plain10
454396537
./Plain10  6.38s user 0.07s system 98% cpu 6.524 total
$ time ./Par10
454396537
./Unl10  2.69s user 0.02s system 99% cpu 2.733 total

Unlifted速い!異常に速い!

満を持して Problem 254

Parallel.Unlifted にも Parallel.Unlifted.Parallel にも product が無かった。ライブラリのソースを見ている内に、productUPを発見する。が、なぜかexportされていない。ので、書き写す。こんなんなった。

{-# LANGUAGE PArr #-}
{-# OPTIONS_GHC -fdph-par #-}
module Main where
import qualified Data.Array.Parallel.Unlifted as U
import System.Environment

toDigits :: Int -> U.Array Int
toDigits 0 = U.empty
toDigits n = 
 let  (d, m) = (n `divMod` 10)
      hds = toDigits d
    in  hds U.+:+ (U.replicate 1 m)

productUP :: U.Array Int -> Int
{-# INLINE productUP #-}
productUP = U.fold (*) 1

forEachDigit f = U.sum . U.map f . toDigits
f = forEachDigit (productUP . U.enumFromTo 1)
sf = forEachDigit id . f
g i = sub 1
  where sub n = if sf n == i then n else sub (n+1)
sg = forEachDigit id . g

sumsg x = U.sum . U.map sg . U.enumFromTo x

main = do args <- getArgs
          case args of
            (x:y:_) -> print $ sumsg (read x) (read y)
            (x:[])  -> print $ sg $ read x
            _       -> putStrLn "use just as you like"

こんぱいる&実行。

$ ghc -Odph -fdph-par Unl254.hs --make -threaded
$ time ./Unl254 2
Unl254: thread blocked indefinitely
./Unl254 2  0.00s user 0.00s system 80% cpu 0.004 total

え・・・っ?

色々試行錯誤した結果、fが全ての元凶であることを突き止める。突き止めたが、どこがわるいのかわからない。多分、forEachDigit や productUP や U.enumFromTo を多重に組み合わせている所為っぽいんだけど、よくわからなかった。歯(があった場所)が痛い。

今日はここでタイムアップ。

こんくるーじょん

  • vectoriseを捨てよ、unliftedに出よう
    • 今のところ、vectorise は unlifted の構文糖。unliftedの方が現状では色々なことが出来る。
    • vectorise は今後のヴァージョンアップでパワーアップする。カバー出来てない機能も沢山ある。
    • vectorise は実装が非効率的なぶぶんもおおい
  • それでもまあ早くなる
  • vectorise は ベクトル化可能部分不可能部分を分離して書かないといけないけど、unliftedは普通に混ぜて書けるので楽
  • しかしvectorise は [: :] を使ってリスト感覚で書ける様になるので今後に期待
  • unlifted でも [: :] でも、Int とか Word8とかDoubleとかしか扱えないのは不便。せめてChar とIntegerよこせ
  • package 機能はクソだと思ってたけど挙動をフラグ一つで切り替えられるのは頭良いとおもった。
  • succ 白石さん*1

[][][] ProjectEulerを Data Parallel Haskell で解いたらえらい遅くなった  ProjectEulerを Data Parallel Haskell で解いたらえらい遅くなった - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  ProjectEulerを Data Parallel Haskell で解いたらえらい遅くなった - はてな使ったら負けだと思っている deriving Haskell

経緯

  1. ProjectEuler 254 を解こうとした
  2. Haskellで素朴に実装してみた
  3. sgの和 がなかなか終わらない
  4. よし、dphやってみよう
  5. 一生終わらなくなった

Source code

Par254.hs(並列処理部分) :

{-# OPTIONS -fvectorise -fdph-par #-}
{-# LANGUAGE PArr #-}
module Par254 where
import Data.Array.Parallel
import Data.Array.Parallel.Prelude
import Data.Array.Parallel.Prelude.Int
import GHC.PArr (toP)
import qualified Prelude 

toDigits :: Int -> [: Int :]
toDigits n  | n == 0 = emptyP
            | otherwise =
 let  d = (n `div` 10)
      m = (n `mod` 10)
      hds = toDigits d
    in  hds +:+ singletonP m

forEachDigit f n = sumP ( mapP (f) ( toDigits n))
f = forEachDigit (¥a -> productP(enumFromToP 1 a))
sf n = forEachDigit (¥a->a) ( f n )
g i = sub 1
  where sub n = if sf n == i then n else sub (n+1)
sg n = forEachDigit (¥a->a) ( g n )

sumsg x y = sumP (mapP sg (enumFromToP x y))

254.hs (表示部分) :

module Main where
import Par254
import System.Environment
main = do xs <- getArgs
          case xs of
            (x:y:_) -> print $ sumsg (read x) (read y)
            (x:_)   -> print $ sg $ read x
            _       -> putStrLn "usage: just you like"

コンパイル:

$ ghc -c -Odph -fdph-par Par254.hs           
$ ghc -Odph -fdph-par 254.hs --make -threaded

実行

$ time ./254 20
15
./254 20  0.10s user 0.11s system 121% cpu 0.174 total
$ time ./254 1 20
 (終わらない)

ぎもんてん

sumsg の定義から sumP を取り除くと早く終わるんだけど、このままだと sumsg 1 2 すら終わらない。何で?

あとCharが使えなかったり、 ポイントフリースタイルが使えなかったり、パターンマッチ出来なかったりdphは発展途上すぎると思いました。

なので、親知らずを抜いてくることにします。

追記

そう云えばsumP抜いたときの結果をわすれてた。

Par254.hs :

sumsg x y = mapP sg (enumFromToP x y)

にsumsgの定義を置き換える。

実行

$ time ./254 1 20
[:1,2,5,6,7,3,4,5,6,7,8,8,9,13,9,10,11,13,14,15:]
./254 1 20  0.52s user 0.56s system 127% cpu 0.853 total

はやい!

[][][]因みに並列じゃないと 因みに並列じゃないと - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク - 因みに並列じゃないと - はてな使ったら負けだと思っている deriving Haskell

$ time ./254plain 1 20
[1,2,5,6,7,3,4,5,6,7,8,8,9,13,9,10,11,13,14,15]
./254plain 1 20  0.00s user 0.00s system 91% cpu 0.007 total

もっとはやかった!

module Main where 
import System.Environment
toDigits :: Int -> [Int ]
toDigits 0 = []
toDigits n = 
 let  d = (n `div` 10)
      m = (n `mod` 10)
      hds = toDigits d
    in  hds ++ [m]

forEachDigit f = sum . map f . toDigits
f = forEachDigit (product . enumFromTo 1)
sf = forEachDigit id . f
g i = sub 1
  where sub n = if sf n == i then n else sub (n+1)
sg = forEachDigit id . g

sumsg x y = map sg $ enumFromTo x y
main = do xs <- getArgs
          case xs of
            (x:y:_) -> print $ sumsg (read x) (read y)
            (x:_)   -> print $ sg $ read x
            _       -> putStrLn "usage: just you like"

と云う訳で、歯医者いってきます

*1:白石さん++ の Haskell

トラックバック - http://haskell.g.hatena.ne.jp/mr_konn/20090905

2009-07-27

[][][] HOCをインストールしようとして出来なかった記録 14:54  HOCをインストールしようとして出来なかった記録 - はてな使ったら負けだと思っている deriving Haskell を含むブックマーク はてなブックマーク -  HOCをインストールしようとして出来なかった記録 - はてな使ったら負けだと思っている deriving Haskell

(追記:2009年09月現在、普通にインストール出来る様になったみたいです! HOCがインストール出来る様になったみたいです - はてな使ったら負けだと思っている deriving Haskell - haskell 参照。)

HOC と云う、HaskellからObjective-Cを叩くためのバインディングがあります。

これは主にghc-6.8.x系統を対象としてるみたいで、GHC-6.10.x系統には手直ししないとインストール出来ないみたいです。と云う訳で、勉強の合間に少しずつ四苦八苦しつつ結局行き詰まった記録。


共有ライブラリとかオブジェクトファイルとかの事をあんまり(と云うか殆んど)知らない状態でやろうと云うのが間違いなのかもしれませんが……こうしてみたら?と云うアドバイスがありましたら是非お願いします。

環境

OSX 10.5.7 + MacBook (1.3GHz Intel Core 2 duo)

$ ghc -v
Glasgow Haskell Compiler, Version 6.10.4, for Haskell 98, stage 2 booted by GHC version 6.10.3.20090628

$ ghc-pkg list
/Library/Frameworks/GHC.framework/Versions/610/usr/lib/ghc-6.10.4/./package.conf:
    Cabal-1.6.0.3, HUnit-1.2.0.3, QuickCheck-1.2.0.0, array-0.2.0.0,
    base-3.0.3.1, base-4.1.0.0, bytestring-0.9.1.4, cinnamon-0.2,
    containers-0.2.0.1, directory-1.0.0.3, (dph-base-0.3),
    (dph-par-0.3), (dph-prim-interface-0.3), (dph-prim-par-0.3),
    (dph-prim-seq-0.3), (dph-seq-0.3), extensible-exceptions-0.1.1.0,
    filepath-1.1.0.2, (ghc-6.10.4), ghc-prim-0.1.0.0, haddock-2.4.2,
    haskell-src-1.0.1.3, haskell98-1.0.1.0, hpc-0.5.0.3, html-1.0.1.2,
    integer-0.1.0.1, mtl-1.1.0.2, network-2.2.1.2, old-locale-1.0.0.1,
    old-time-1.0.0.2, packedstring-0.1.0.1, parallel-1.1.0.1,
    parsec-2.1.0.1, pretty-1.0.1.0, process-1.0.1.1, random-1.0.0.1,
    regex-base-0.72.0.2, regex-compat-0.71.0.1, regex-posix-0.72.0.3,
    rts-1.0, stm-2.1.1.2, syb-0.1.0.1, template-haskell-2.3.0.1,
    time-1.1.4, unix-2.3.2.0, xhtml-3000.2.0.1
/Users/*****/.ghc/i386-darwin-6.10.4/package.conf:
    HaXml-1.13.3, binary-0.5.0.1, darcs-2.2.1, fgl-5.4.2.2,
    haskeline-0.6.1.6, parsec-3.0.0, plugins-1.4.1, terminfo-0.2.2.1,
    terminfo-0.3.0.2, utf8-string-0.3.5

$ cabal -V
cabal-install version 0.6.2
using version 1.6.0.3 of the Cabal library 

libffi のビルド & HOCをもってくる

$ svn co http://hoc.googlecode.com/svn/trunk/libffi
$ cd libffi/src
$ ../configure --prefix=/usr/local CFLAGS=-DMACOSX
$ make
$ sudo make install
$ svn co http://hoc.googlecode.com/svn/trunk/hoc

ビルドする

BUILDING.CVS と云うファイルがあるが、しかし、つかわれているのはSVNだしなぁ……と思いつつ書かれている通りにやってみる。

$ ./autogen.sh 
+ exec autoconf
$ ./configure
checking build system type... i386-apple-darwin9.7.0
checking host system type... i386-apple-darwin9.7.0
checking target system type... i386-apple-darwin9.7.0

 = snip =

config.status: creating HOC_cbits/Makefile
config.status: creating InterfaceGenerator/Makefile
config.status: creating InterfaceGenerator2/Makefile
config.status: creating Tools/Makefile

$ make
for dir in HOC_cbits HOC InterfaceGenerator InterfaceGenerator2 Bindings Foundation AppKit Tools docs ; do (cd $dir; echo "In directory $dir"; make all) done
In directory HOC_cbits

  = snip= 

[13 of 31] Compiling HOC.Exception    ( HOC/Exception.hs, build/objects/HOC/Exception.o )

HOC/Exception.hs:73:23: Not in scope: `throwDyn'

HOC/Exception.hs:82:8: Not in scope: `catchDyn'

HOC/Exception.hs:88:26: Not in scope: `catchDyn'
make[1]: *** [ghcmake.build-stamp] Error 1

  = snip =

../inplace.conf: openBinaryFile: does not exist (No such file or directory)
make[1]: *** [hocwrap] Error 1
In directory docs
pod2html \
	  --index \
	  --header \
	  --title="HOC: a Haskell to Objective-C bridge" \
	  --outfile "HOC.html" \
	  Introduction.pod Quick_Start.pod Mapping_Types.pod Accessing_Other_Frameworks.pod Creating_an_Objective-C_Class_in_Haskell.pod Tools.pod Appendices.pod

どうやら例外処理の方法が古いみたいで Control.OldException にしないと駄目っぽいな……と当りをつけて、HOC/HOC/Exception.hs の import Control.Exception を import Control.OldException に変えて再挑戦。

$ make
[27 of 31] Compiling HOC.Utilities    ( HOC/Utilities.hs, build/objects/HOC/Utilities.o )
[28 of 31] Compiling HOC.StdArgumentTypes ( HOC/StdArgumentTypes.hs, build/objects/HOC/StdArgumentTypes.o )
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Loading package unix-2.3.2.0 ... linking ... done.
Loading object (static) ../HOC_cbits/HOC_cbits.o ... done
Loading object (dynamic) objc ... done
Loading object (dynamic) ffi ... done
Loading object (framework) Foundation ... done
ghc: 
unknown symbol `_setIvarInList'
final link ... linking extra libraries/objects failed
make[1]: *** [ghcmake.build-stamp] Error 1

  = snip =

../inplace.conf: openBinaryFile: does not exist (No such file or directory)
make[1]: *** [hocwrap] Error 1
In directory docs
make[1]: Nothing to be done for `all'.

むう。だめだ。

Setup.hs に乗り換える

そもそも.CVSなんて信用するから悪かったんだ!と逆ギレして、 しばらくオンラインをさまよう。

すると、何故か Yi の READMEに詳しく載っていたので、の云う通りにしてビルドしてみる。cabal-install で色々パッケージを管理しているので、 --prefix=/usr/local の代わりに --user をつける。

$ runhaskell Setup configure --user
Configuring HOC-1.0...
$ runhaskell Setup build           
Compiling HOC_cbits...

  = snip =

[11 of 31] Compiling HOC.Exception    ( HOC/HOC/Exception.hs, dist/build/HOC/Exception.o )

HOC/HOC/Exception.hs:23:28:
    Not in scope: type constructor or class `SomeException'

  = snip = 

HOC/HOC/Exception.hs:51:19:
    Not in scope: data constructor `SomeException'

おっと。何か新しいExceptionに対応したみたい(?)なのでさっきの OldException を再びException に戻す。

$ runhaskell Setup build
Compiling HOC_cbits...
Preprocessing library HOC-1.0...
Preprocessing executables for HOC-1.0...

  = snip =

[30 of 31] Compiling HOC.CStruct      ( HOC/HOC/CStruct.hs, dist/build/HOC/CStruct.o )
[31 of 31] Compiling HOC              ( HOC/HOC.hs, dist/build/HOC.o )
Warning: the following files would be used as linker inputs, but linking is not being done: HOC_cbits.o
ghc: no input files
Usage: For basic information, try the `--help' option.

ここで止まってしまう。見るとdist/build/HOC_cbits.oは生成されている模様だが……。

HOC_cbits との闘争

ここで良くわからなくなったので、 本家のGoogle Code で似た様な報告が無いか見る。

あった。Issue 12

どうやら、6.10.xのcabal君が HOC_cbits.o をCのソースファイルとして扱うとかなんとかで、兎に角上手くいかないらしい。

折角インストールした6.10.xを6.8.xに戻すのはイヤなので二つ目の方法を取る。つまり、

A slightly less quick workaround is to remove the 'c-sources' line in the .cabal file and manually link HOC_cbits.o into the build product (both the .a and the .o).

http://code.google.com/p/hoc/issues/detail?id=12

と云うことなので、 HOC.cabal から 67行目の

c-sources: HOC_cbits.o   

を削除。再び、

$ runhaskell Setup configure --user
Configuring HOC-1.0...
$ runhaskell Setup build           
Compiling HOC_cbits...
Preprocessing library HOC-1.0...
Preprocessing executables for HOC-1.0...
Building HOC-1.0...

  = snip =

[26 of 27] Compiling Output           ( InterfaceGenerator2/Output.hs, dist/build/hoc-ifgen/hoc-ifgen-tmp/Output.o )
[27 of 27] Compiling Main             ( InterfaceGenerator2/Main.hs, dist/build/hoc-ifgen/hoc-ifgen-tmp/Main.o )
Linking dist/build/hoc-ifgen/hoc-ifgen ...

となって平穏無事に終わった。やった。

さて、HOC_cbitsをちゃんとつくる。下の方のComment 5 で出てたpatch を当てる。

The attached patch lets me build with base 4.0.0 (using Control.OldExceptions) with some hand-tweaking of

libHOC_cbits.dylib:

(略)

(will build HOC_cbits and HOC, then fail)

(略)

copy libHOC_cbits.dylib to ~/.cabal/lib (or elsewhere in my ghc-pkg config library path)

(make sure HOC_cbits is an extra library in ghc-pkg's HOC package descriptor)

http://code.google.com/p/hoc/issues/detail?id=12

手順通りやってみる。

$ ./configure OBJCFLAGS='-arch i686' CFLAGS='-arch i686' LDFLAGS='-arch i686'  --with-dynamic-haskell CFLAGS='-fpic -dynamic' OBJCFLAGS='-fpic -dynamic' LDFLAGS='-fpic -dynamic -read_only_relocs suppress'

$ make
for dir in HOC_cbits HOC InterfaceGenerator InterfaceGenerator2 Bindings Foundation AppKit Tools docs ; do (cd $dir; echo "In directory $dir"; make all) done

  = snip =

../inplace.conf: openBinaryFile: does not exist (No such file or directory)
make[1]: *** [hocwrap] Error 1
In directory docs
make[1]: Nothing to be done for `all'.

$ ls HOC_cbits | grep HOC
HOC_cbits.o
libHOC_cbits.a
libHOC_cbits.dylib

$ cp HOC_cbits/libHOC_cbits.{a,dylib} HOC_cbits/HOC_cbits.o ~/.cabal/lib 
$ runhaskell Setup.hs configure --user
$ runhaskell Setup.hs build
$ sudo runhaskell Setup.hs install
Installing library in /Users/hiromi/.cabal/lib/HOC-1.0/ghc-6.10.4
Installing executable(s) in /Users/hiromi/.cabal/bin
Registering HOC-1.0...
Reading package info from "dist/installed-pkg-config" ... done.
Writing new package config file... done.

~/.ghc/i386-darwin-6.10.4/package.conf を確認。extraLibraries に HOC_cbits が入っていない。修正。

つれないBinding

$ cd Bindings
$ sudo sh make-bindings-macos.sh
*** Processing Framework Foundation ***
Parsing Objective-C header files:                       [                         ]

  = snip =

Writing Foundation.pi:                             100% [*************************]
done.
~/install/hoc/Bindings/Generated/HOC-Foundation ~/install/hoc/Bindings/Generated
cabal: These flags are used without having been defined: base4

む。と云うことで、手作業でやってみることにする。

$ cd Generated/HOC-Foundation
$ emacs HOC-Foundation.cabal

 ( 四行目の Flag base を Flag base4 に変更)

$ sudo runhaskell Setup.hs configure --user
Warning: HOC-Foundation.cabal: A package using section syntax should require
"Cabal-Version: >= 1.2" or equivalent.
Configuring HOC-Foundation-1.0...

$ sudo runhaskell Setup.hs build           
Preprocessing library HOC-Foundation-1.0...
Building HOC-Foundation-1.0...
[  1 of 154] Compiling Foundation.NSXMLNodeOptions ( Foundation/NSXMLNodeOptions.hs, dist/build/Foundation/NSXMLNodeOptions.o )

  = snip = 

Loading package template-haskell ... linking ... done.
Loading package HOC-1.0 ... <command line>: can't load .so/.DLL for: HOC_cbits (dlopen(libHOC_cbits.dylib, 9): image not found)

あれ?移した場所が悪かったのかな…… と云うことで:


$ sudo mv ~/.cabal/lib/libHOC_cbits.{dylib,a}  ~/.cabal/lib/HOC_cbits.o ~/.cabal/lib/HOC-1.0/ghc-6.10.4
$ sudo runhaskell Setup.hs build
Preprocessing library HOC-Foundation-1.0...
Building HOC-Foundation-1.0...
[  1 of 154] Compiling Foundation.NSXMLNodeOptions ( Foundation/NSXMLNodeOptions.hs, dist/build/Foundation/NSXMLNodeOptions.o )

  = snip =

Loading package template-haskell ... linking ... done.
ghc: 
unknown symbol `_setIvarInList'
Loading package HOC-1.0 ... linking ... ghc: unable to load package `HOC-1.0'

こうなった……。うーん。

このIRCログ の 02:04:34 付近をみてみて、HOC/HOC/NewClass.hs の 58行目〜62行目

foreign import ccall "NewClass.h makeIvarList"
    rawMakeIvarList :: Int -> IO (Ptr IvarList)
foreign import ccall "NewClass.h setIvarInList"
    rawSetIvarInList :: Ptr IvarList -> Int
                  -> CString -> CString -> CSize -> Word8 -> IO ()

foreign import ccall "Ivars.h makeIvarList"
    rawMakeIvarList :: Int -> IO (Ptr IvarList)
foreign import ccall "Ivars.h setIvarInList"
    rawSetIvarInList :: Ptr IvarList -> Int
                  -> CString -> CString -> CSize -> Word8 -> IO ()

と書き換えて、

$ runhaskell Setup.hs clean
$ runhaskell Setup.hs configure --user
$ runhaskell Setup.hs build
$ runhaskell Setup.hs install

などとして、上書きされてしまうので、再び ~/.ghc/i386-darwin-6.10.4/package.conf を適宜弄ってから、もう一度

$ cd Bindings/Generated/HOC-Foundation
$ sudo runhaskell Setup.hs configure --user
$ sudo runhaskell Setup.hs build

などとしてみるも、またも unknown symbol `_setIvarInList' と云われる。……むーん……

参考文献s

  1. hoc - Project Hosting on Google Code
    1. Issue 12
  2. Yi の README
  3. Chat Logs
  4. このIRCログ

どなたかこうしてみたら?と云うアドバイスなどありましたらよろしくおねがいします><

トラックバック - http://haskell.g.hatena.ne.jp/mr_konn/20090727