Help us understand the problem. What is going on with this article?

MacでHaskellを使ってJavaScriptをビルドする環境(GHCJS)を最短で整える(長い)

More than 1 year has passed since last update.

はじめに

先日、Elmを試したけどあまり合わなかった。
HTMLをプログラムで生成せずに昔ながらのやり方でファイルに書きたい。それでいて型指定も欲しい。
なのでGHCJSを試した。

GHCJSはElmのような新しい言語があるのではなく、Haskellのコンパイラ出力をJavaScriptに差し替えるというなかなか凄いやり方。Haskellの機能がほとんどそのまま使えて、更にDOMの機能も仕様の定義から自動生成されている。
自動生成なので ByteLengthQueuingStrategy のようなマイナー(?)なクラスにも対応するHaskellの関数があったりする。

つまり機能不足で困ることはほとんど無いと予想できる。
その代わりに提供されているのは素のJavaScriptのHaskell版というようなものなので、追加で miso のようなフレームワークを使った方が開発速度は速いと思う。

Haskellの環境を整える

brew install haskell-stack

StackはHaskell版brewみたいなもの。すぐにインストールできる。

取りあえず最低限のプログラムを実行してみる。

stack new haskell-sample --resolver=nightly-2018-05-10 

--resolver無しの場合は最新版LTSのコンパイラがインストールされる。取りあえずここはあとで使うのでバージョン指定で。

cd haskell-sample
stack build

.stack-work/install/x86_64-osx/nightly-2018-05-10/8.4.2/bin/haskell-sample-exe

someFuncと出れば成功。
これでHaskellの環境は終了。

Haskell版のコンパイル

続いてJavaScriptを書く環境を作る。

と言ってもサンプルプロジェクトをビルドするだけ。
https://github.com/nishimura/ghcjs-form-sample1.git
ここに置いた。

git clone https://github.com/nishimura/ghcjs-form-sample1.git
cd ghcjs-form-sample1
stack build

ghcjs-dom のコンパイルで固まったように見えるが落ち着いて待つ。メモリも大量に持って行かれるが、最初に書いたようにDOMで使う関数や型の大半をHaskell用に頑張って変換しているところなので。

ちなみに MacBook Pro で43分かかった。

コンパイルされたHaskell版プログラムを実行する。

.stack-work/install/x86_64-osx/nightly-2018-05-10/8.4.2/bin/ghcjs-form-sample1-exe

これでWebサーバーが立ち上がったので、 http://localhost:8000/ にアクセスして確認する。
これはHaskellで動いている状態。

JavaScript版のコンパイル

次はJavaScriptで動かす。

最初に~/.stack/config.yamlにバージョン差異をスルーする設定を書く。

echo 'allow-newer: true' >> ~/.stack/config.yaml

それからGHCJSのコンパイルに使うツールをインストールする。

stack install alex
stack install happy

export PATH="$HOME/.local/bin:$PATH"

PATHはbashrc等にも追加する。

node.jsも無ければインストール。

brew install node

そしてビルド。

stack build --stack-yaml=js.yaml

GHCJS自体のコンパイルに38分かかった。それから続いてghcjs-bootでGHCJSで使うライブラリのコンパイルが始まり、追加で44分かかった。

この時点で .stack-work/install/x86_64-osx/nightly-2018-05-10/ghcjs-8.4.0.1_ghc-8.4.2.20180505/bin/ghcjs-form-sample1-exe.jsexe/ にJavaScriptが生成されている。

open .stack-work/install/x86_64-osx/nightly-2018-05-10/ghcjs-8.4.0.1_ghc-8.4.2.20180505/bin/ghcjs-form-sample1-exe.jsexe/index.html

ブラウザで確認すると、コンソールに

id="area" not found

と出ていればOK。
htmlが自動生成されたものになっているので、まだ動かない。

今回利用するHTMLファイルはhtml/index.htmlにある。

JavaScriptのコンパイル(圧縮)

Closure Compilerの起動に必要なjavaのjreをここから手に入る。
https://www.java.com/ja/download/
完全にブラウザ用としてインストールされるっぽいけども気にせずこれを使う。

ln -s /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java ~/bin/java

javaをPATHの通っているところに置いたあと、JavaScriptのコンパイルしてブラウザでアクセス。

./compile.sh

open html/index.html

これで終了。

Haskellのプログラムの説明

Main.hsAppMain.hsを起動するだけ。

本体は

application :: HTMLDocument -> HTMLElement -> JSM ()
application doc area = do
    Just body <- getBody doc
    releaseClick <- on area G.click $ do
        (x, y) <- mouseClientXY
        newParagraph <- createElement doc "p"
        text <- createTextNode doc $ "Click " ++ show (x, y)
        _ <- appendChild newParagraph text
        _ <- appendChild body newParagraph
        return ()

    -- Make an exit button
    exitMVar <- liftIO newEmptyMVar
    exit <- createElement' doc TagSpan
    text <- createTextNode doc "Click here to exit"
    _ <- appendChild exit text
    _ <- appendChild body exit
    releaseExit <- on exit G.click $ liftIO $ putMVar exitMVar ()

    -- Force all all the lazy evaluation to be executed
    syncPoint

    -- In GHC compiled version the WebSocket connection will end when this
    -- thread ends.  So we will wait until the user clicks exit.
    _ <- liftIO $ takeMVar exitMVar
    releaseClick
    releaseExit
    setInnerHTML body "<h1>Ka kite ano (See you later)</h1>"
    return ()

こんな感じ。

https://github.com/ghcjs/jsaddle
ここのサンプルプログラムを写したんだけど、結構書き換えないと動かなかった。インターフェースをカジュアルに変えるのはHaskellあるあるっぽい。

Helper.hsElement => HTMLElement 変換用。
型が厳密すぎて、getElementById で取ってきた ElementHTMLElement ではないので GlobalEventHandlers のonclickイベントが付けられない。つらい。

まとめ

コンパイル待ち時間を抜いたら15分ぐらい。
コンパイル待ち時間を入れたら2時間ぐらいでセットアップできた。
(実は長時間待った後の最後にエラーが出てやり直したりして5時間ぐらいかかっている)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした