heimdalの技術ノート

IT全般、Linux、Windows、プログラミング開発など、何でもござれ

PureScript + psc-package + Halogen + Electronでサンプルを動かす

f:id:xheimdal:20180527153213p:plain

はじめに

前回の記事ではElm + Electronの組み合わせでサンプルを作成した。

heimdal.hatenablog.com

今回はPureScript + Electronの組み合わせでサンプルを作ってみる。

ElmとPureScriptのそれぞれの特徴や比較など。

Elm

関数型JSフレームワークとしてシンプルで分かりやすく関数型の良さをフロントエンドに取り入れたい時に、軽く取り入れることが出来る。初心者でも導入しやすい。
Elm専用パッケージ管理コマンドでパッケージを導入するが、パッケージがそこまで多くはない。が、フロントエンド特化のパッケージなので、基本何でもできる。 Haskellライクな書き方ができるが、Haskellのような特性を活かしたコーディングは出来ない。あくまで純粋関数の特性を活かせるものと思った方が良いかもしれない。 Elmのパッケージhtmlのprogramを使ってMVUモデルを作っていく感じでコーディングしていく。 Electronとの相性だが、現在パッケージが少なく、Portsという仕組みを使ってnode_modulesへアクセスすることになるため、完全にJavaScriptから抜け出せないため、 若干Electronの相性がまだよくないかもしれない。今後もパッケージが増えていくと思うので、だんだんElectronとの相性もよくなっていくと考える。 仮想DOMを扱っているので、最近のJSフレームワーク同様高速で動き、再描画が優れている。

PureScript

Elmと比べて、Haskellの特性により近づけた関数型JSフレームワーク
Elmより複雑で難しく初心者には向かない。Haskellの基礎またはElmのいずれかを軽く触ってからPureScriptを触ると入りやすい。
Elmよりもパッケージが豊富でElectronやSqlite3を操作するものがあったりとElectronとの相性が良いと言える。が、パッケージが多すぎて何を使うべきか、分かりづらい。
調べた限り、Domというパッケージがあって簡単にDOM操作出来そうだが、Halogenの方が多機能で仮想DOMを使っているので、難しくてもHalogenを使った方が良い気がする。

加えて、基本はpurescriptにパッケージを追加する際はbowerを使うが今後主流になると思われるpsc-packageを使って実装してみる。

と、いうことで今回は題名の通り、PureScript + psc-package + Electron + Halogenを使ってみる。

環境

mac
electron 2.0.2
node.js 10.0.1
purescript 0.12 psc-package 0.3.2

フォルダ構成

f:id:xheimdal:20180613013907p:plain

手順

electronとpurescriptのパッケージを導入

npm i -g electron purescript psc-package

electronの初期設定は過去記事を参照

heimdal.hatenablog.com

purescriptの初期ファイル生成

pulp --psc-package init

psc-package.jsonのバージョンを以下に揃える

{
  "name": "electron-purescript-halogen-sample",
  "set": "psc-0.12.0",
  "source": "https://github.com/purescript/package-sets.git",
  "depends": [
    "prelude",
    "halogen"
  ]
}

※setの箇所は、purescript/package-sets.gitの最新版に書き換えると、install出来るパッケージが増やすことが出来る。

基本パッケージとhalogenパッケージをダウンロードする

psc-package install

srcフォルダのソースコードを以下のソースにおきかえる。

Halogenのサンプルソース使ってソースコードにコメントを挿入している。

github.com

ソースコード

Main.purs

module Main where

-- 基本コマンドモジュール
import Prelude
import Effect (Effect)
import Halogen.Aff as HA
import Halogen.VDom.Driver (runUI)
import Button as B

main :: Effect Unit
-- 非同期処理のHalogenを実行する
main = HA.runHalogenAff do
  -- DOMが読み込まれるのを待ってから、Body要素を取得してセット
  body <- HA.awaitBody
  -- bodyに対して、ボタン要素をマウント
  runUI B.myButton unit body

Button.purs

module Button where

-- いつものやつモジュール
import Prelude

-- 値が入っていないのを許容するモジュール
import Data.Maybe (Maybe(..))
-- halogenの基本モジュール
import Halogen as H
-- halogenのHTML要素モジュール
import Halogen.HTML as HH
-- halogenのEvent系を取り扱うモジュール
import Halogen.HTML.Events as HE
-- helogenのプロパティを表現するモジュール
import Halogen.HTML.Properties as HP


-- ボタンの状態を表す
type State = Boolean

-- クエリ代数
-- Queryとは、type: -> type:で、常に型変数を持っている
data Query a
  -- アクションのコンストラクタ
  = Toggle a
 --   リクエストのコンストラクタ
  | IsOn (Boolean -> a)

-- 親コンポーネントがあった場合に使うが、今回使わないのでUnitを使用する
type Input = Unit

-- コンポーネントに必要な出力メッセージを定義する。
-- 今回はボタン切替と状態を返す
data Message = Toggled Boolean

myButton :: forall m. H.Component HH.HTML Query Input Message m
myButton =
  H.component
    { initialState: const initialState
    , render
    , eval
    , receiver: const Nothing
    }
  where

  initialState :: State
  initialState = false

  render :: State -> H.ComponentHTML Query
  render state =
    let
      label = if state then "On" else "Off"
    in
        HH.div_
            [ HH.text "Hello PureScript トグルボタン"
            ,HH.div_
                [HH.button
                    [ HP.title label
                    , HE.onClick (HE.input_ Toggle)
                    ]
                    [ HH.text label ]
                ]
            ]

  eval :: Query ~> H.ComponentDSL State Query Message m
  eval = case _ of
    Toggle next -> do
      state <- H.get
      let nextState = not state
      H.put nextState
      H.raise $ Toggled nextState
      pure next
    IsOn reply -> do
      state <- H.get
      pure (reply state)

コンパイルする

pulp build -t output.js
index.htmlが読み込めるように、jsとして出力する。

index.htmlを編集する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>hello purescript!!</title>
</head>
<body>
    <script src="./output.js"></script>
</body>
</html>

実行する

electron .

ソースコード

ここからダウンロードすれば使えるようになる。詳しくは README参照。 github.com

まとめ

今回は、PureScript + psc-package + Halogen + Electronを使ったサンプルを作ってみたが、Halogenのサンプルが難解で完全に解析出来ていないのが心残りになってしまった。。。
動くものという意味で、公式のサンプルを作ってみたが、アレンジする場合は、Halogenをもっと知る必要があるため、実用的な動きが出来る段階になるまでの学習コストはかなり高い。
またbowerというパッケージ管理コマンドを使わずにpsc-packageでHalogenを扱えるようになったのは大きいと思う。
もう少し自由にコーディング出来るようになった時にまた記事にしたい。