Elmで青春のフラクタルを描画する【Elmアドベントカレンダー2014 6日目】

これはElm Advent Calendar 2014の6日目です。

Elmの優れた点と言えば、

  1. Haskellベースの静的型関数型言語
  2. Functional Reactive Programming
  3. 強力なレイアウト機能

などがまず挙がるでしょう。しかし、

  1. 図形描画ライブラリ

も、実はElmの長所の一つなのです。

Elmの図形描画機能

まずは 公式サイトの例をご覧ください。

HTML5Canvasは、JavaScriptによる手続き型の実装を前提にしていたので、コマ毎にclearRectで消去したり、save/restoreで状態を管理したりする必要がありました。プログラマーはともかく、「学校で習った図形を書いてみよう」と言う場合にはやや非直感的です。

一方Elmでは更新処理自体はElmがやってくれます。 また、図形はFormという「図形そのもの」を表す型として、 図形の移動や拡大縮小はmove scaleといった関数として、自然に表現できます。

なお、注意点とがあり、collage w h forms でw×hのキャンバスを作ったとき、原点はキャンバスの中心で、Xが大きくなるほど右、Yが大きくなるほど上の座標です。つまり、数学と同じ座標なのです。

フラクタルを書いてみる

図形が描ける、ならば初めにフラクタルを描くものと相場は決まっています。再帰呼出し&縮小+移動で、シェルピンスキーのギャスケットを描画してみました!

Sierpinski gasket with Elm

n = 0では単なる三角形を、n > 0では自分自身の縮小コピー×3を描画しています。

なお、Elmでは小さすぎる図形(1ピクセル未満?)は描画してくれないらしく、上の例で再帰が深すぎると何も表示されなくなりました。

自分語り&妄想:Elmは未来の十進BASIC

フラクタルを書いていると、十年前私がまだ紅顔の少年だった頃を思い出します。

私がプログラマーになったのは図書館から↓の本を借りたのがきっかけでした。

当時は悪名高いMicrosoft Visual Basicがボロクソに言われつつも全盛の時代でしたが、十進BASICVBとは全く別の"Full Basic"という規格に沿った教育用BASIC環境でした。

教育用だけあって

と、初心者用の機能が充実しているだけではなく、

  • 数値は10進小数がデフォルト(2進数モードもある)
  • 10進1000桁モード(数値型が小数点以下1000桁まで格納可能になる)
  • 有理数モード
  • 複素数モード
  • 強力な図形描画機能
    • function文とは別に、図形を定義するpicture文がある
    • scale, rotate, shift, 任意のアフィン変換, それらの合成変換
  • グラフ描画用関数
  • 図形関連の豊富なExample(含シェルピンスキーのギャスケット)

等など、数学用の機能がとても充実していました。

言うまでもなく、構造化プログラミングにも対応しており、良いプログラミングを教われなくなったり完膚なきまでに壊れることもありませんでした。

さすがに今では、プログラマー志望の高校生にはPythonあたりを薦めます。 でも、数学者・科学者志望だけどプログラミングの素養を身につけたい高校生ならば、今でも10進BASICを薦めたいと思います。(高校生の知り合いはいないけど)。

一方Elmですが、Playground、デバッガ、図形ライブラリなどは既にかなりの充実度です。 このまま発展すれば21世紀版・十進BASIC的な地位に着けるのではないか? と、ガスケットを書いていて思いました。

あとがき

おかげさまでElm Advent Calendar 2014は今のところ途切れず続いていますが、まだまだ空きがあります。ふるってご参加ください。

フラクタルで描く 魅惑的な画像の世界

フラクタルで描く 魅惑的な画像の世界

ElmとHaskellの6つの非互換性【Elmアドベントカレンダー2014 3日目】

これはElm Advent Calendar 2014の3日目です。

f:id:doloopwhile:20141204015840p:plain

ElmはHaskellベースの言語なので、Haskellの記事や入門書もElmの学習に使えます。しかし、実はElmにはチョコチョコHaskellと違うところがあって互換性はありません。

相違点をElm公式サイトLearnSyntaxFAQから抜粋して解説します。

1. Elmは遅延評価ではありません

Haskellは遅延評価なのでリスト処理の効率が良いのがウリでした。 JavaScriptにもジェネレータという形で遅延評価の思想が取り入れられています。

しかし、Elmのリストや辞書等などは遅延評価ではありません。

Elmはクライアントサイドの言語なので、遅延評価があまり役立たないこと、 SignalJavaScriptのジェネレータのような機能を持っているためのようです。

2. Elmにモナドはありません

Signalがあるので、IOモナドは不要です。それどころかMaybeモナドもListモナドもありません。したがって、do文もありません。

なお、Maybeは標準ライブラリにMaybeパッケージがあり、JavaScriptnullを使うようなコードを安全に書けます。

でも、やっぱりdo文はあった方がいいと私は思うことがあります・・・。caseを5段階ぐらいネストしたりすると。

まぁ「モナド圏論に基づいています*1」という説明が「Haskellってモナドが難しいんでしょ?」という誤解を生むことがないからいいのかな?

3. 関数関連の演算子が違う

Elm Haskell 意味 同値なJavaScript
f x <| g y z f x $ g y z (f x (g y z)) f(x, g(y, z))
g y z |> f x なし
(f << g) x (f . g) x (f (g x)) f(g(x))
(g >> f) x なし

$.がそれぞれ<|<<に変わっています。そして引数の順番を逆にした>|>>が加わっています。

4. :::の意味が逆

Elm Haskell
norm : Float -> Float -> Float
norm x y = sqrt (x * x + y * y)
norm :: Float -> Float -> Float
norm x y = sqrt (x * x + y * y)
1 :: 2 :: alist == [1, 2] ++ alist
1 : 2 : alist == [1, 2] ++ alist

これは型アノテーションの方がリストへの追加よりも頻繁に使うからです。

5. foldl系関数の引数の順序が逆

List.foldr, List.foldl はリストの要素に次々に関数を適用する関数です。 データ構造毎にDict.foldlSet.foldlといった変種もあります。他言語ではreduceinjectという名前で提供されていることがあります。

// elm
v = List.foldr (+) 0 [1,2,3]

// 同等なJS
var list = [1, 2, 3];
var v = 0;
for (var i = 0; i < list.length; i++) {
  v = v + list[i];
}

微妙な違いですがElmではList.foldlが受け取る関数の引数の順序をあえてHaskellと逆にしてあります。

Elm Haskell
List.foldl : (a -> b -> b) -> b -> [a] -> b List.foldl : (b -> a -> b) -> b -> [a] -> b
List.foldr : (a -> b -> b) -> b -> [a] -> b List.foldr : (a -> b -> b) -> b -> [a] -> b

これは、Elmでは標準ライブラリを「データ構造はいつも最後の引数」というルールで設計しているのに関係しています。

たしかに、HaskellのようにList.foldl : (b -> a -> b) -> b -> [a] -> bであったとすると、foldrにはそのまま使えるのにfoldlには使えない関数が出てきて困る気がしますが・・・私にはよく分かりませんでした。

公式サイトのLibrary Design Guidelinesにその点が書いてあるのですが、例がおかしい(間違っている?古い?)気がします。

6. 他にも色々なものがありません。

  • do文以外のモナド関連の構文や演算子
  • where文。代わりにletを使う
  • (+1) のような記法。代わりに((+) 1)と書く
  • ライブラリ

こんなに違って大丈夫?

Haskellは元もと演算子が多い(多すぎる?)言語なので、ElmではSignalが入って互換性がなくなるなら演算子も整理したいと言うことなのでしょう。

ただ「HaskellのライブラリがElmに流用できない」という懸念をもつ方もいらっしゃると思います。私も不安です。

しかし、Haskellは主にサーバーサイドやコンパイラなどに使われ、Elmはクライアントサイドのための言語なので、「ブラウザ上で動くコンパイラを作りたい」というのでもなければ、HaskellのライブラリをElmに流用する機会は実は無いのかもしれません。

あとがき

更新が24時を過ぎてしまいました。

Elm Advent Calendar 2014にはまだ空きがあります。ふるってご参加ください。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

*1:確かにモナドは圏論にルーツがありますが、 「『変数』はデカルトにルーツがあります」「正規表現はオートマトン理論に基づいています」「while文を使用することで原始計算可能な関数だけでなく真に計算可能な関数を作成することが可能になります」ってあえて説明したりしないでしょう?「モナドは圏論(ry」が使われる背景には、数学という虎の威を借りてHaskellを高尚に見せようとする心理があるように私には思えます。

ソースに一行追加するだけで実行時にバイナリをバルスできるモジュールを書いた

バズーカが弾切れ、絶対絶命のピンチ、メガネの小悪党がmain関数を実行する前に実行ファイルを削除したいというのはよくあることです。

使用例

たとえばこういうコードに・・・

// get.go
package main

func main() {
   println("ハハハハハ!飛行石を手に入れたぞー!!")
}

importをくわえるだけで・・・

// get.go
package main

import (
  _ "github.com/doloopwhile/go-balse"
)

func main() {
   println("ハハハハハ!飛行石を手に入れたぞー!!")
}

たったこれだけでバルスします!

$ go build get.go
$ ./get
panic: _人人人人人_
      > バルス!<
        ̄Y^Y^ Y^Y^ ̄

goroutine 16 [running]:
runtime.panic(0x44cc20, 0xc208000120)
    /usr/local/go/src/pkg/runtime/panic.c:279 +0xf5
github.com/doloopwhile/go-balse.init·1()
    /home/kenjiomoto/gocode/src/github.com/doloopwhile/go-balse/balse.go:9 +0x87
github.com/doloopwhile/go-balse.init()
    /home/kenjiomoto/gocode/src/github.com/doloopwhile/go-balse/balse.go:10 +0x4b
main.init()
    /home/kenjiomoto/Downloads/get.go:10 +0x46

goroutine 17 [runnable]:
runtime.MHeap_Scavenger()
    /usr/local/go/src/pkg/runtime/mheap.c:507
runtime.goexit()
    /usr/local/go/src/pkg/runtime/proc.c:1445

goroutine 18 [runnable]:
bgsweep()
    /usr/local/go/src/pkg/runtime/mgc0.c:1976
runtime.goexit()
    /usr/local/go/src/pkg/runtime/proc.c:1445

goroutine 19 [runnable]:
runfinq()
    /usr/local/go/src/pkg/runtime/mgc0.c:2606
runtime.goexit()
    /usr/local/go/src/pkg/runtime/proc.c:1445

$ ./get
bash: ./get: そのようなファイルやディレクトリはありません

doloopwhile/go-balse

あとがき

最初は「importするだけでカレントディレクトリのファイルを全て削除」という仕様にしたところ、うっかり「README.mdがー!!README.mdがー!!」となってしまったので、実行ファイルを削除するだけのオトナシイ仕様にしました。

「ソースに一行追加するだけで・・・」は本来もっと真面目な目的で利用できるものです。

ElmのインストールとHelloWorldのコンパイル【Elmアドベントカレンダー2014 2日目】

ElmはPlaygroundで簡単にコードを試せますが、最終的にはelmコマンドでコンパイルする必要があります。

0. バイナリをインストール

MacとWindowは、公式サイトのInstallから、 バイナリをダウンロードできます。

しかし、LinuxではHaskell Platformを使ってソースからインストールしなければなりません。

1. Haskell Platformのインストール

Haskell Platformapt-getなどでインストールできますが、古い場合があるので新しくインストールした方が良いかもしれません.

cd /
curl -L https://www.haskell.org/platform/download/2014.2.0.0/haskell-platform-2014.2.0.0-unknown-linux-x86_64.tar.gz | sudo tar xvzf -
sudo /usr/local/haskell/ghc-7.8.3-x86_64/bin/activate-hs

2. cabalのアップデートとElmのインストール

cabal update
cabal install cabal-install
cabal install -j Elm-0.13 elm-repl-0.3 elm-reactor-0.1 elm-get-0.1.3

echo 'export PATH=$PATH:~/.cabal/bin' >> ~/.bashrc

3. Hello Worldコンパイル

cd 【適当なディレクトリ】
echo 'main = plainText "hello"' > hello.elm
elm hello.elm
firefox build/hello.html

Elmとはどんな言語か?その7つの特徴【Elmアドベントカレンダー2014 1日目】

f:id:doloopwhile:20141129182134p:plain

Elmはクライアントサイド向けの関数型言語です。一体どんな言語なのでしょうか?

1. HTML/CSS/JSにコンパイルされる

ElmはHTML/CSS/JSにコンパイルして使用します。 この辺はJavaScriptコンパイルされるAltJSに似ています。 AltJS同様、Elmも特別なランタイムライブラリは不要です。

ただし、Elmは1つのソースでHTML/CSS/JSをすべてまかなえるのが特徴です。

2. 独自の抽象化レイヤーを提供している

代表的なAltJSであるCoffeeScriptは"It's just JavaScript"をモットーにしています。すなわち、文法を改良するのみで、「中身」はJavaScript以上でも以下でもありません。

一方、Elmでは直接JSやCSSを操作することはできません。 基本的にすべてElmの関数・レイアウトライブラリを使わなければなりません。

これはとっつきにくいのですが、逆に見れば「歴史的なしがらみによる汚さ」を扱う必要が無いと言うことです。

ただし、Portsを通じてJavaScriptとやりとりしたり、Canvas風にHTMLに埋め込んで使うこともできます。

3. Haskellベースである

ElmはHaskellを元に若干の修正を加えた独自言語です。

当然Elmは強力な静的型を備えています。 「0に.nameというプロパティはありません」的なエラーとは無縁です。

またあらゆるものは関数ですArray.some Array.every どころか、foldl, map, etc.. 使い放題です。

4. FRP(Functional Reactive Programing)に対応している

ElmはHaskellベースなので状態や変数はありません。

もちろん、クライアントには状態変化がありまくりです。 マウスの動き、ウィンドウサイズの変更、APIからのレスポンス etc...

JavaScriptの場合は状態変化はonmousemoveなどのcallbackを使って関数で明示的に扱うことになります。しかし、callbackが増えてきて、しかもネストしだすと何がなんだかわからなくなります。callback地獄です。

一方Elmでは組み込みの<~~を使います。

main =
  renderScene
  <~imageAPIResp
  ~ Mouse.position
  ~ Window.width
  ~ pagingAPIResp
  ~ (fps 30)

更新の処理はElmが勝手に行ってくれます。

FRPについてはこちらのスライドが大変優れているので、こちらをご覧ください。

http://www.slideshare.net/maedaunderscore/elmfunctional-reactive-programming

5. Playgroundが充実している

ElmはPlaygroundに力を入れています。

f:id:doloopwhile:20141129200243p:plain

GoなどにもPlaygroundはありますが、Elmのは、

と、かなり機能が充実しています。

また、Exampleも基本文法から、応用までそろっています。

Examples

ElmのPlaygroundのゆるかわほっこり感は、Haskellの取っ付きにくさを打ち消してあまりあるものがありますね。

6. Haskellだけど怖くない

私はHaskellはこんなイメージです。 f:id:doloopwhile:20141129201705j:plain

確かに、JavaScriptとはシンタックスが全然違うのでとっつきにくくはあります。 最初はコンパイルが通らなくて苦労します。

しかし、ElmはPlaygroundで簡単にトライ&エラーを繰り返す事ができます。 また、「最初の100個の素数を求める」のような人工的な例ではなく、目に見えて実際に動くアプリを作る事ができるので、Haskellよりモチベーションを保ちやすいでしょう。

また、ElmにはFRPがある代わりにElmにモナドはありません。

7. ElmはAnglarJSを置き換える言語である?

既にクライアントサイドには様々なAltJSやライブラリがあるのに、なぜ新しくElmを学ぶのか?

私の場合はAngularJSへの疑問でした。

AngularJSは優れたライブラリで、実際に仕事で使ってもいるんですが、 難しすぎる!

  • 3つの異なる言語(HTML/CSS/JavaScript)を同時に扱わなくてはならない。*1難しい!
  • 特定の使われ方(CRUD)に特化していて、汎用性が欠けているように見える→難しい!
  • ngBind, ngResource, ngDirective etc… 多い!→難しい!

WEBは10年20年先も存在し続けると思いますが、AngularJSが10年使われるか疑問です。

一方、Elmはシンプルです。

  • 言語は1つだけ*2
  • FRPの仕組みは汎用的(フォーム入力にも、APIコールにも、アニメーションにも何にでも使える)
  • 「すべてが関数」関数は容易に組み合わせられる

ひょっとしたら、Elmにはクライアントサイドの未来の主流になるかもしれません。 少なくともElmにヒントがあるのは確かです。

関連リンク

POSTDにElm関連の翻訳記事があります。

まず、これらをよんでモチベーションを高めてから始めるといいと思います。

あとがき

明日2日目は「Elmをインストールする」です。

Elm Advent Calendar 2014にはまだ空きがあります。ふるってご参加ください。

*1:場合によってはCoffeeScript、Less、Slimなども学ばなければなりません。

*2:基本的には。HTMLやJavaScriptと連携すれば別です。

~/.bashrcは何も出力してはいけない(するならエラー出力に)

知ってて当たり前の超凡ミス。

起こったこと:rsyncが失敗する

rsyncが何故か失敗してしまう。"protocol incompatibility"と出ていますが、--protocolオプションを指定しても、最新版のrsyncをインストールしても、同じエラーが出ます。なぜでしょう?

Tue Nov  4 12:15:46 2014 Normal: recursive startup rsync: /home/xxxxxxx/Documents/src/xxxxxxx/ -> vagrant@deva:xxxxxxx/ excluding
/cucumber/vendor
/cucumber/.bundle
~/.bashrc loaded
protocol version mismatch -- is your shell clean?
(see the rsync man page for an explanation)
rsync error: protocol incompatibility (code 2) at compat.c(174) [sender=3.1.0]
Tue Nov  4 12:15:47 2014 Error: Failure on startup of "/xxxxxxxxx".

原因:~/.bashrcでechoしている

上のログに "~/.bashrc loaded" という行があります。これが原因です。.bashrc*1の最後の行で"echo"していました。もちろん、echo以外でも、何かしらの出力をしてしまうとscpやrsyncはコケます。

# ~/.bashrcの最後の行
echo "~/.bashrc loaded"

これと同じ問題です。

bash - SCP doesn't work when echo in .bashrc? - Stack Overflow

How the SCP protocol works (Jan Pechanec's weblog)によると、 scpは初めにリモート側にプロトコルを問い合わせるのですが、~/.bashrcが何かを出力するとそれが混乱してしまうようです。多分rsyncも同様なのでしょう。

回避策:Greetingは標準エラー出力に出す

echo "~/.bashrc loaded" >&2

*1:安直に、どのOSにもデフォルトで入っているBashを使っています。本物のプログラマならバギーで文法が汚く補完が中途半端なbashではなく、堅牢で高速なdashですよね?分かっています

Goplay: Goで手軽に書捨て環境を作るツール

goreの説明を追記。

f:id:doloopwhile:20040712133434j:plain

LLから、Goに移るときの不満の一つに「標準のREPL(インタラクティブシェル)が無い」があります。

「ポインタはmapのキーになるんだっけ?」のような、つまらない(けど、ありがちな)疑問を解消するには、ドキュメントを漁るより、コードを書いてしまう方が、手っ取り早いですよね?

goplayを作った!

goplayは、書捨てGoコードを書くためのシンプルなツールです。

https://camo.githubusercontent.com/db725471459165d7d24b6e6f234b02267b187746/68747470733a2f2f7261772e6769746875622e636f6d2f646f6c6f6f707768696c652f676f706c61792f6d61737465722f64656d6f2e676966

やっているのは、 1. 一時ディレクトリを/tmp/goplay/以下に作る 2. main.goを一時ディレクトリに作る 3. 一時ディレクトリで新しいシェルを起動 4. 環境変数GOPATHに一時ディレクトリを追加。 これだけです。

インストールはgo getするだけです。

go get github.com/doloopwhile/goplay

他の方法との比較

ちゃんとしたREPL(gore)を使う

いや、正直、普通はgoreのほうがいいと思います。良い子はgoreを使いましょう。

エディタで書捨てコードを書きたい子はgoplayを使いましょう。

github.com

Go Playground

Go Playgroundはブラウザ上でGoを書いて実行できます。 しかし、

  • 外部ライブラリをimportできない
  • 好きなエディタが使えない という欠点があります。

goplayは、ローカルで実行するので、外部ライブラリもエディタも自由に使えます。

その場でvi a.goする

まぁ、それでも動くは動くんですが、

package main*.goファイルが同一ディレクトリに複数あると、 エディタのシンタックスチェッカーが、

「main redeclared in this block」

を出してくるのが五月蝿い。

そして、作ったスクリプトを削除するのも面倒くさい。

Let's go!