Haskell」タグアーカイブ

Haskell でオレオレ Num クラスを作るための考察

以前の記事に書いたように、 Haskell 標準の Num クラスには色々と不満がある:

Haskell の Num クラスに対する不満

結論としては「過去に遡って Num クラスの定義を変えるしかない」だったわけだが、時間干渉も因果律への反逆もできない我々としてはその選択肢は現実的ではないので、独自の Num クラス(オレオレ Num クラス)を作ることにする。この記事は、筆者がオレオレ Num クラスを作った際に考えたことをまとめたものである。

続きを読む

雰囲気で並列プログラミングをやってみる

Haskell での並列プログラミングをマスターしたい、とは言わないが、「全然わからない、俺たちは雰囲気で並列プログラミングをやっている」程度のことは言えるようになりたい。

今回は「データ構造の各要素を並列で評価する」ことを目指す。以下の内容は筆者がいい加減な知識で書いているので、そういうつもりでテキトーに読んでほしい。 続きを読む

Haskell (GHC) の型レベル自然数とリフレクションを試してみる

【2020年4月20日追記】GHCの型レベル自然数については、より網羅的な記事を書いた。型レベル自然数についてより詳しく知りたい方は、こちらの記事も参照いただきたい:GHCの型レベル自然数を理解する【追記終わり】

最近のGHCでは、自然数をパラメーターとするデータ型を定義できる。固定長リストの長さを型に持たせるとか、行列の大きさを型に持たせるとか、そういうことができる。あるいは、自然数 m に対して Z/mZ を表す型を作ることもできる。m を素数とすればこれは有限体となる。

実際に、型レベル自然数を使って Z/mZ に相当する型を作ってみよう。(名前は FiniteField とした) 続きを読む

Haskell で高速なプログラムを書くときに注意すること

Haskell は表現力が高いプログラミング言語だが、気をつけないと非効率的なコードが生成されてしまうことがある。では、どういうところに気をつければ高速なコードになるのか。少し調べてみた。

この記事に書くのは、あくまで原則とかそういうやつなので、コンパイラーの最適化(正格性解析、インライン化、自動ボックス化解除)によっては、自前で工夫しても意味がない、つまり、コンパイラーに任せた場合と同じ結果になるかもしれない。どういう場合に早くなるかはケースバイケースなのだ。 続きを読む

Haskell の Num クラスに対する不満

Haskell には Num という型クラスがあって、足し算とか掛け算の基本的な演算はここで定義されている。標準ライブラリで定義されているインスタンスとしては、 Int, Integer, Rational, Complex a などがある。

一言で言えば Num クラスは環っぽいものに対応するのだが、いろいろとアレな点がある。これを設計した人は、インスタンスとして整数、有理数、浮動小数点数、複素数くらいしか想定していないのではないか。 続きを読む

Cabal sandboxのメモ

Cabalを使ってHaskellのパッケージ管理をやっていると、依存関係がもつれて酷い事になる。あるプロジェクトで使うパッケージと別のプロジェクトで使うパッケージが干渉したりとかなんかそういうアレだ。こういう時はsandboxを使うと良いらしい。マニュアル

自分のプロジェクトのディレクトリで

$ cabal sandbox init

を実行すれば、新しいsandboxができる。何かパッケージを入れようと思ったら、そのディレクトリで普通に

$ cabal install ほげほげ

を実行すればそのパッケージはそのディレクトリに入る。

sandbox内のパッケージを使ってghciを起動したい場合は、

$ cabal repl

を使う。

sandbox内のパッケージを使ってghcやrunghcなどのコマンドを叩きたい場合は、1.20.0で追加された(?)execコマンドを使うといいようだ。

$ cabal exec ghc -- --make hogehoge.hs

この記事はcabal-install 1.21.0.0の時点の情報をテキトーに調べた結果を書いたので、もしかしたらもっと正しい使い方とかあるのかもしれないが、そんなもん知らん。

(9月21日: typoを修正)

さくらのレンタルサーバーにGHC+Cabalを導入した

環境

  • さくらのレンタルサーバー スタンダード
  • FreeBSD 9.1-RELEASE-p15 (x86_64)
  • もちろんroot権限なし
  • ここでは、シェルはbashを使う(デフォルトはcshだった気がする)
  • ここでは、プレフィックスは $HOME/usr にする($HOME/usr/bin とか $HOME/usr/lib にファイルが入るということ)

1. GHCをインストールする

FreeBSD向けにはHaskell Platformは出ていない。しかしGHCのバイナリは配布されているので、ここからとってくる。この記事を書いている2014年7月6日の時点では、最新バージョンは7.8.2である。

$ curl -O http://www.haskell.org/ghc/dist/7.8.2/ghc-7.8.2-x86_64-portbld-freebsd.tar.xz
$ tar xf ghc-7.8.2-x86_64-portbld-freebsd.tar.xz
$ cd ghc-7.8.2
$ ./configure --prefix=$HOME/usr

この後に gmake install をするのだが、ghc-pkgやghcが使っている共有ライブラリにパスが通っていないので、 LD_LIBRARY_PATH を設定してやる。…なんかもっといい方法があると思う。

$ echo "export LD_LIBRARY_PATH=$HOME/usr/lib/ghc-7.8.2/terminfo-0.4.0.0:$HOME/usr/lib/ghc-7.8.2/bin-package-db-0.0.0.0:$HOME/usr/lib/ghc-7.8.2/binary-0.7.1.0:$HOME/usr/lib/ghc-7.8.2/Cabal-1.18.1.3:$HOME/usr/lib/ghc-7.8.2/process-1.2.0.0:$HOME/usr/lib/ghc-7.8.2/pretty-1.1.1.1:$HOME/usr/lib/ghc-7.8.2/directory-1.2.1.0:$HOME/usr/lib/ghc-7.8.2/unix-2.7.0.1:$HOME/usr/lib/ghc-7.8.2/time-1.4.2:$HOME/usr/lib/ghc-7.8.2/old-locale-1.0.0.6:$HOME/usr/lib/ghc-7.8.2/filepath-1.3.0.2:$HOME/usr/lib/ghc-7.8.2/containers-0.5.5.1:$HOME/usr/lib/ghc-7.8.2/bytestring-0.10.4.0:$HOME/usr/lib/ghc-7.8.2/deepseq-1.3.0.2:$HOME/usr/lib/ghc-7.8.2/deepseq-1.3.0.2:$HOME/usr/lib/ghc-7.8.2/array-0.5.0.0:$HOME/usr/lib/ghc-7.8.2/base-4.7.0.0:$HOME/usr/lib/ghc-7.8.2/integer-gmp-0.5.1.0:$HOME/usr/lib/ghc-7.8.2/ghc-prim-0.3.1.0:$HOME/usr/lib/ghc-7.8.2/rts-1.0:$HOME/usr/lib/ghc-7.8.2/haskeline-0.7.1.2:$HOME/usr/lib/ghc-7.8.2/ghc-7.8.2:$HOME/usr/lib/ghc-7.8.2/transformers-0.3.0.0:$HOME/usr/lib/ghc-7.8.2/template-haskell-2.9.0.0:$HOME/usr/lib/ghc-7.8.2/hpc-0.6.0.1:$HOME/usr/lib/ghc-7.8.2/hoopl-3.10.0.1" > ~/ghc-ld-path.sh
$ source ~/ghc-ld-path.sh
$ gmake install

2. Cabalをインストールする

このへんからCabalのソースコードを落としてくる。

$ curl -O http://www.haskell.org/cabal/release/cabal-install-1.20.0.2/cabal-install-1.20.0.2.tar.gz
$ tar xf cabal-install-1.20.0.2.tar.gz
$ cd cabal-install-1.20.0.2
$ emacs bootstrap.sh

本来なら cabal-install-* の中にある bootstrap.sh を実行すればいいのだが、sedの仕様の違いかなんだか知らないがリンカのコマンドがおかしなことになるので、テキストエディタで bootstrap.sh を編集してリンカを ld に決め打ちしてやる。

LINK=/usr/bin/ld

また、そのままだと tar が /dev/sr0 だかなんだかにアクセスして死ぬので、ググって出てきた情報を元に tar の代わりに tar -f - を使うようにする。これは bootstrap.sh を書き換えなくても環境変数で上書きできる。

あと、Haddockでドキュメントを生成するのがうまくいかなさそうな気配を感じたので、 --no-docをつけてドキュメントを生成しないようにする。

$ TAR="tar -f -" ./bootstrap.sh --no-doc

これでインストールされるはず。

環境変数 PATH がcabalとかを見に行くように、.bash_profile をいじってやる。

$ emacs ~/.bash_profile
export PATH=$HOME/.cabal/bin:$HOME/usr/bin:$PATH

あとは普通にcabalが使える。

$ cabal update
$ cabal install cabal-install

3. せっかくなのでCabalでいろいろインストールしてみる

なんとなくIdrisを入れてみよう!と思った。依存関係がよくわからないが、コマンドとしては

$ cabal install --upgrade-dependencies --force-reinstalls idris

ぐらいでいけるはず。…だが現実はそううまくはいかない。

遭遇した問題としては大きく分けて二つあって、テンポラリディレクトリ /var/tmp 以下で作業してるのがなぜか実行権限が与えられなくてconfigureとかでこけるのと、ソースコード中に非US-ASCII文字が入っていてこけるの。

前者は、テンポラリディレクトリを自前で用意したディレクトリに変えれば良い。つまり ~/tmp 的なディレクトリを作ってやって、環境変数 TMPDIR=~/tmp とすればよい。

後者は、 hGetContents が失敗する的なエラーが出る。

hGetContents: invalid argument (invalid byte sequence)

file コマンドで問題のファイルを見るとUS-ASCIIではなくUTF-8になっているのが分かる。そこで、問題のファイルから非US-ASCII文字を探し出してやって、それをUS-ASCII文字で置き換えてやればファイルがUS-ASCIIになって hGetContents で問題なく読み込める…という方法でビルドしたが、冷静に考えてHaskellはUnicode対応してるはずだし hGetContents でUTF-8を読み込めないのがおかしい。終わった後に気づいたが、おそらく文字エンコーディングが設定されていない的な問題な気がするので、 LANG=en_US.UTF-8 あたりを指定すれば良かった。

結局、

$ mkdir ~/tmp
$ TMPDIR=~/tmp LANG=en_US.UTF-8 cabal install --upgrade-dependencies --force-reinstalls idris

とすれば深いこと考えずにビルドできるはず。

以前やった時はどこかで挫折してしまっていたが、今回は気合いでなんとか頑張った。普段OS X と (Arch) Linux ばかり触っていて、 FreeBSD はよくわからないのだが、まあなんとかなった。

あとはレンタルサーバーの環境にnode.jsが入れば楽しそうなのだが…。

共用サーバーに処理系をいろいろ入れていろいろやりたいというのが間違いで、そういうのがしたいならVPSでも契約しろというのは正論だろう。