LoginSignup
10
9

More than 5 years have passed since last update.

Haste + Parsec でブラウザ上で動く電卓をつくる

Last updated at Posted at 2015-12-19

Haste は haskell で書いたら javascript にコンパイルできて超ハッピーというアレです.

Haste のいいところの1つは,haskell で書かれたライブラリも使いうる1というところです.Haskell ですごく便利なライブラリで,ブラウザ上で動かしたいかもしれないものといえば Parsec を思いついたので,Haste と Parsec を組み合わせてブラウザ上で動く電卓を作ってみましょう.

準備

パーザを書く

まずは後で haste を使うことは忘れて,普通に haskell で Parsec を使って電卓に使えるパーザを書きましょう.今回は最終的に Double の計算をすることとします. expr :: Parsec String u Double という型のものができればオッケーです(名前は何でもいいですが,以下 expr はこの函数を指します).ここは Haste に関係ないので省略しますが,Haskell 構文解析 超入門 という記事が非常にお勧めです2

Parsec に不慣れなのでだいぶ不安なのですが,以前上の記事を読んだ記憶を頼りにとりあえず加減乗除と括弧に対応したものを書きました.

Parse.hs
module Parse (expr) where
import Text.Parsec

-- |
-- >>> parseTest expr "1 + 2*(3+5)"
-- 17.0
expr :: Parsec String u Double
...
--- このあといい感じの実装

Haste 側の準備

ghc とは別に,Haste が Parsec を呼べるようにしておく必要があります.最近の Haste だと haste-cabal というツール3が同梱されており,これを使ってこのへんの管理をします.以前 haskell 環境を作りなおして以来若干大胆なので,sandbox とか使わずにインストールしてしまいましょう.

terminal
$ haste-cabal install parsec

Haste 側の準備はこれだけ(!)です.次で完成します.

書き書き

実装はどうしましょうか.シンプルな電卓ということ,折角 parsec を使うこと,などを考えてこういうふうにしてみます:

  1. <input type="text" .. > に計算したい式を打ち込む
  2. keyUp で計算を試みる.成功したらそれを表示,失敗したら parsec のエラーを表示.
  3. 表示は div.textContent4

html を用意します.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Calc</title>
  <script src="./build/calc.js"></script>
</head>
<body>
  <div>
    <input type="text" name="calcInput" id="calcInput">
  </div>
  <div id="output"></div>
</body>
</html>

あとは,「keyUp#calcInput の中身を呼んで,さっき書いたパーザに渡した結果を #output に書き込む」という部分を,Hasteimport したうえで haskell で書いてやればおしまいです.

Calc.hs
import Haste
import Haste.DOM
import Haste.Events
import Text.Parsec

-- さっき書いたパーザ
import Parse (expr)

main = do
    -- elemById ::  MonadIO m => ElemID -> m (Maybe Elem)
    Just input <- elemById "calcInput"
    Just output <- elemById "output"
    -- onEvent element event (EventData evt -> m ()) という形
    -- この場合 e にはイベント情報が入り,(今回は使っていないが)函数に渡される
    onEvent input KeyUp $ \ e -> do
        -- getValue :: (IsElem e, MonadIO m, JSType a) => e -> m (Maybe a)
        Just ln <- getValue input
        case parse expr "" ln of
             Right s -> setProp output "textContent" (show s)
             Left l -> setProp output "textContent" (show l)

コンパイルすれば終わり

terminal
$ hastec ./src/Calc.hs -isrc -o ./build/calc.js
$ firefox index.html

結果はこちらです(github.io)keyUp で動くのでちょっと動きが面白い.

まとめ

Haste と Parsec の組み合わせで簡単にブラウザ上で動く電卓を作ることができました.この辺りのライブラリを使えることで加速するうれしさというものもあると思いますし,場合によっては昔 ghc でコンパイルするために書いたソースを流用することもできましょう.積極的に使っていきましょう.


  1. 残念ながらTemplate haskell にはまだ対応していないので,それを使っているライブラリ(lens など.cf: オートマトンで遊ぶやつを作った - Just $ A sandbox) は使えません.また hashable も現時点ではbuild できないようです. 

  2. それに限らず,この方の入門記事は全般に大変為になるので,いくつか目を通すといいと思います. 

  3. 名前の通り cabal からの fork ですが,普通に haste をインストールするときは haste-boot で降ってきます. 

  4. 最初 innerText つかってて,firefox でエラーも吐かずに動かなくてちょっと悩みました. 

10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9