Elmで青春のフラクタルを描画する【Elmアドベントカレンダー2014 6日目】
これはElm Advent Calendar 2014の6日目です。
Elmの優れた点と言えば、
などがまず挙がるでしょう。しかし、
- 図形描画ライブラリ
も、実はElmの長所の一つなのです。
Elmの図形描画機能
まずは 公式サイトの例をご覧ください。
HTML5のCanvasは、JavaScriptによる手続き型の実装を前提にしていたので、コマ毎にclearRectで消去したり、save/restoreで状態を管理したりする必要がありました。プログラマーはともかく、「学校で習った図形を書いてみよう」と言う場合にはやや非直感的です。
一方Elmでは更新処理自体はElmがやってくれます。
また、図形はForm
という「図形そのもの」を表す型として、
図形の移動や拡大縮小はmove
scale
といった関数として、自然に表現できます。
なお、注意点とがあり、collage w h forms
でw×hのキャンバスを作ったとき、原点はキャンバスの中心で、Xが大きくなるほど右、Yが大きくなるほど上の座標です。つまり、数学と同じ座標なのです。
フラクタルを書いてみる
図形が描ける、ならば初めにフラクタルを描くものと相場は決まっています。再帰呼出し&縮小+移動で、シェルピンスキーのギャスケットを描画してみました!
n = 0
では単なる三角形を、n > 0
では自分自身の縮小コピー×3を描画しています。
なお、Elmでは小さすぎる図形(1ピクセル未満?)は描画してくれないらしく、上の例で再帰が深すぎると何も表示されなくなりました。
自分語り&妄想:Elmは未来の十進BASIC
フラクタルを書いていると、十年前私がまだ紅顔の少年だった頃を思い出します。
私がプログラマーになったのは図書館から↓の本を借りたのがきっかけでした。
パソコンを遊ぶ簡単プログラミング―コンピュータを自由に操る「十進BASIC」入門 CD-ROM付 (ブルーバックス)
- 作者: 木村良夫
- 出版社/メーカー: 講談社
- 発売日: 2003/01/20
- メディア: 新書
- クリック: 7回
- この商品を含むブログ (2件) を見る
当時は悪名高いMicrosoft Visual Basicがボロクソに言われつつも全盛の時代でしたが、十進BASICはVBとは全く別の"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は今のところ途切れず続いていますが、まだまだ空きがあります。ふるってご参加ください。
- 作者: 白井豊
- 出版社/メーカー: ゆたか創造舎
- 発売日: 2009/09/05
- メディア: 単行本(ソフトカバー)
- クリック: 9回
- この商品を含むブログを見る
ElmとHaskellの6つの非互換性【Elmアドベントカレンダー2014 3日目】
これはElm Advent Calendar 2014の3日目です。
ElmはHaskellベースの言語なので、Haskellの記事や入門書もElmの学習に使えます。しかし、実はElmにはチョコチョコHaskellと違うところがあって互換性はありません。
相違点をElm公式サイトLearnの SyntaxとFAQから抜粋して解説します。
1. Elmは遅延評価ではありません
Haskellは遅延評価なのでリスト処理の効率が良いのがウリでした。 JavaScriptにもジェネレータという形で遅延評価の思想が取り入れられています。
しかし、Elmのリストや辞書等などは遅延評価ではありません。
Elmはクライアントサイドの言語なので、遅延評価があまり役立たないこと、
Signal
がJavaScriptのジェネレータのような機能を持っているためのようです。
2. Elmにモナドはありません
Signal
があるので、IOモナドは不要です。それどころかMaybeモナドもListモナドもありません。したがって、do文もありません。
なお、Maybeは標準ライブラリにMaybe
パッケージがあり、JavaScriptでnull
を使うようなコードを安全に書けます。
でも、やっぱりdo文はあった方がいいと私は思うことがあります・・・。case
を5段階ぐらいネストしたりすると。
まぁ「モナドは圏論に基づいています*1」という説明が「Haskellってモナドが難しいんでしょ?」という誤解を生むことがないからいいのかな?
3. 関数関連の演算子が違う
Elm | Haskell | 意味 | 同値なJavaScript | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
f x <| g y z
|
$
と.
がそれぞれ<|
と<<
に変わっています。そして引数の順番を逆にした>|
と>>
が加わっています。
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.foldl
、Set.foldl
といった変種もあります。他言語ではreduce
やinject
という名前で提供されていることがあります。
// 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
|
これは、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にはまだ空きがあります。ふるってご参加ください。
- 作者: Miran Lipovača,田中英行,村主崇行
- 出版社/メーカー: オーム社
- 発売日: 2012/05/23
- メディア: 単行本(ソフトカバー)
- 購入: 25人 クリック: 580回
- この商品を含むブログ (67件) を見る
*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: そのようなファイルやディレクトリはありません
あとがき
最初は「importするだけでカレントディレクトリのファイルを全て削除」という仕様にしたところ、うっかり「README.mdがー!!README.mdがー!!」となってしまったので、実行ファイルを削除するだけのオトナシイ仕様にしました。
「ソースに一行追加するだけで・・・」は本来もっと真面目な目的で利用できるものです。
ElmのインストールとHelloWorldのコンパイル【Elmアドベントカレンダー2014 2日目】
ElmはPlaygroundで簡単にコードを試せますが、最終的にはelm
コマンドでコンパイルする必要があります。
0. バイナリをインストール
MacとWindowは、公式サイトのInstallから、 バイナリをダウンロードできます。
しかし、LinuxではHaskell Platformを使ってソースからインストールしなければなりません。
1. Haskell Platformのインストール
Haskell Platformはapt-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日目】
- これはElm Advent Calendar 2014の1日目です。
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に力を入れています。
GoなどにもPlaygroundはありますが、Elmのは、
と、かなり機能が充実しています。
また、Exampleも基本文法から、応用までそろっています。
ElmのPlaygroundのゆるかわほっこり感は、Haskellの取っ付きにくさを打ち消してあまりあるものがありますね。
6. Haskellだけど怖くない
私はHaskellはこんなイメージです。
確かに、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はシンプルです。
ひょっとしたら、Elmにはクライアントサイドの未来の主流になるかもしれません。 少なくともElmにヒントがあるのは確かです。
関連リンク
まず、これらをよんでモチベーションを高めてから始めるといいと思います。
- 【翻訳】フロントエンドのプログラミング言語をゼロから設計した理由 | POSTD
- 【翻訳】抽象化と組み合わせができるレイアウト言語があれば、CSSは必要ない | POSTD
- 【翻訳】爆速HTML – Elmでの仮想DOM | POSTD
あとがき
明日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
Goplay: Goで手軽に書捨て環境を作るツール
goreの説明を追記。
LLから、Goに移るときの不満の一つに「標準のREPL(インタラクティブシェル)が無い」があります。
「ポインタはmapのキーになるんだっけ?」のような、つまらない(けど、ありがちな)疑問を解消するには、ドキュメントを漁るより、コードを書いてしまう方が、手っ取り早いですよね?
goplayを作った!
goplayは、書捨てGoコードを書くためのシンプルなツールです。
やっているのは、
1. 一時ディレクトリを/tmp/goplay/
以下に作る
2. main.go
を一時ディレクトリに作る
3. 一時ディレクトリで新しいシェルを起動
4. 環境変数GOPATH
に一時ディレクトリを追加。
これだけです。
インストールはgo get
するだけです。
go get github.com/doloopwhile/goplay
他の方法との比較
ちゃんとしたREPL(gore)を使う
いや、正直、普通はgoreのほうがいいと思います。良い子はgoreを使いましょう。
エディタで書捨てコードを書きたい子はgoplayを使いましょう。
Go Playground
Go Playgroundはブラウザ上でGoを書いて実行できます。 しかし、
- 外部ライブラリをimportできない
- 好きなエディタが使えない という欠点があります。
goplayは、ローカルで実行するので、外部ライブラリもエディタも自由に使えます。
その場でvi a.go
する
まぁ、それでも動くは動くんですが、
package main
の*.go
ファイルが同一ディレクトリに複数あると、
エディタのシンタックスチェッカーが、
「main redeclared in this block」
を出してくるのが五月蝿い。
そして、作ったスクリプトを削除するのも面倒くさい。
Let's go!