Jelly v0.9 をリリースしました
はじめに
この記事は PureScript アドベントカレンダー 14 日目の記事です。
PureScript アドベントカレンダー 2022 はこちら
PureScript の UI ライブラリ、 Jelly について、前回記事からしばらく経ったので現状と導入したもの / 知見をまとめます。
前回記事はこちら
ライブラリを分けた
に分けました。正直そんなにメリットを感じていないのと、開発がやりにくいので router
以外は戻して良いかなぁと思っています
ドキュメントサイト
見た目を良い感じにしました。
Core Concepts / Custom Monad の章まで書いています。それ以降はまだです。
el
関数を作った
要素毎に専用の el "div" [] do ...
を
div [] do ...
と書けるようにしました。なお定義している場所はここです
とても面倒くさかった
ルーターの仕様を単純化
単純な構成にして、既存のルーティングツールと統合しやすくしました。
Router を導入すると以下の 3 つの関数が使えるようになります。
pushState :: String -> Effect Unit
replaceState :: String -> Effect Unit
currentRoute :: Signal String
単純ですね! purescript-routing-duplex
との組み合わせがよさそうです。
SSG 機能を消し飛ばした
render 関数があるので
render :: forall m. MonadRec m => MonadHooks m => Component m -> m (Signal String)
これを使って SSG してください、ということにしました。実際ドキュメントサイトもこれを使っています。
Hooks について
Hooks はこんな感じで使えます。
component = hooks do
-- ここに Hooks
pure do
-- ここにコンポーネント
Hooks Everywhere...?
イベントハンドラを Effect
から Hooks に変更しました。
component = do
button [ on click \event -> {- ここで Hooks が使える -} ] do
text "Click me"
これによって、ハンドラ内で後述するカスタムモナドが使えるようになりました。
また、アプリケーション全体のエントリポイントも Hooks で書いて、runHooks
で実行するようになっています。カスタム Hooks が何処でも使えるのは大きいのではないでしょうか。
カスタムモナド
大きな変更点として、Hooks にカスタムモナドを使えるようになりました。具体的には MonadHooks
クラスが実装された型は Hooks として使えるようになります。
class MonadEffect m <= MonadHooks m where
-- | Add a cleaner
useCleaner :: Effect Unit -> m Unit
-- | Unwrap a Signal
useHooks :: forall a. Signal (m a) -> m (Signal a)
instance MonadHooks m => MonadHooks (ReaderT r m) where
useCleaner = lift <<< useCleaner
useHooks sig = do
r <- ask
lift $ useHooks $ flip runReaderT r <$> sig
ReaderT
を使えば deriving
で MonadHooks
にできるので、大域的な状態を持つ Hooks が作れます。
実際にルーターやドキュメントサイトで使用しています。
newtype AppM a = AppM (ReaderT AffjaxDriver (RouterT Hooks) a)
derive newtype instance MonadHooks AppM
大域的な状態として AffjaxDriver
を持っていますが、これはドキュメントサイトが Static Site を生成するときと、ブラウザ側で動くときに、異なる AffjaxDriver
を使う必要があるからです。
まとめ
短い記事になってしまいましたが、カスタムモナドを使えるようにしたのは大きいです。これによって自由度がかなり上がったのではないでしょうか。今後の予定は以下の通りです
- 配列の効率的な表示
- 現状、配列の並べ替えなどの操作に対しては配列全てを描画しなおすしか表示方法がありません。React のように、配列の並べ替え / 要素の追加を効率的に行えるようにします
- O(n) での DOM 更新
- 現在、要素の追加 / 削除時 "子要素の数 n に対して" O(n^2) の更新時間がかかっています (なお、テキストの更新などはテキストの長さを無視すれば O(1) です)。React などの差分検出 O(n) は "全ての要素の数 n に対して" なので単純比較はできませんが、子要素の数が増えても対応できるように、O(n) にするべきです。
これらを追加し、ベンチマークなどをして React などとの性能比較をしてから、正式版 1.0.0 をリリースしようと思います。
Discussion