purescript/package-setsにコントリビュートしてみた(とUIライブラリの宣伝)

package-setsとは

package-setsとは、公式が管理している、各パッケージのリポジトリやバージョンが記載されたセットです。 このpackage-setsにおいて各パッケージのバージョンはただひとつだけ含まれており、package-setsに含まれたものはコンパイルが保証されています。package-sets自体にもバージョンのような概念があり(といってもタグなんですが)、package-setsが更新されたりすると新しくタグがきられます。

PureScriptのパッケージ管理はBowerという印象が強い方がまだ多くいますが、psc-packagespagoというものがあり、これらは指定したpackage-setsとタグをもとにパッケージをインストールできます。

余談ですが、この仕組みは裏を返せばpackage-setsにないものは管理できないということであり、かつてはpackage-setsに自分でパッケージを追加するか、forkして編集したpackage-setsを使うかしかなかったのですが、spagoというパッケージマネージャ兼ビルドツールが出てきて、package-setsをforkしなくてもpackage-setsにないパッケージを管理できるようになりました。

願望も含まれてますが、わたし的にはspago一強になるのではないかと思ってます。

さて、余談はさておき、そんなpackage-setsですが、package-setsに登録されているライブラリが増えるのはとても良いことだと思います。

最近package-setsにパッケージを追加したので、今日はpackage-setsの追加の仕方を紹介します。

前提条件

package-setsは基本的にどんなパッケージでも追加はウェルカムだそうです。 ただし、現時点では、以下の基準を満たしてなければいけません。

  • pulp versionをバージョニングに使用すること
  • pulp publishをつかってパッケージを公開すること
  • bower -i -pでインストールしたパッケージが正しく動くこと

pulpとは昔からあるビルドツールです。

pulp versionを使ってほしい理由は、タグがvから始まってほしいからだそうです。pulp versionvから始まるタグをつけてくれます。 pulp publishとは、パッケージをBowerにあげて、ドキュメントをPursuitというドキュメントサイトにアップロードをするコマンドです。配布方法がpackage-setsだけではなくBowerも存在しているためにこのコマンドで配布してね、ということみたいです。 bower -i -pでインストールしたパッケージが正しく動くことを期待するのも上記の理由でしょう。

とまぁまだBowerの存在感がしっかり残っています。

現状だと、事実上、ライブラリを書く時のみ、pulpとbowerは捨てにくいです。

しかしながら、以下のような動きが始まっており、ゆっくりと時間をかけてBowerの存在感がなくなっていくことを期待しましょう。

github.com

準備

package-setsにpackageを追加する前に以下の依存をインストールしておきましょう。

  • dhall
    • コマンド的な意味だとdhallとdhall-to-jsonが必要です
  • psc-package
  • jq
  • purs (purescriptのコンパイラ)

私の場合は、こういうスポットで使うとき用の雑なDockerイメージをもっていて、それをちょっと改変して使いました。 雑ですがインストール作業すらしたくなければ適宜変更して使ってください。

FROM node:10.14.0

ENV HOME /home/node

RUN set -ex \
  && apt-get update \
  && apt-get install -y lsof jq \
  && cd /tmp \
  # Install dhall
  && curl -fsSL -O https://github.com/dhall-lang/dhall-haskell/releases/download/1.21.0/dhall-1.21.0-x86_64-linux.tar.bz2 \
  && tar xvjf dhall-1.21.0-x86_64-linux.tar.bz2 && mv bin/dhall /usr/local/bin/dhall \
  && chmod +x /usr/local/bin/dhall \
  # Install dhall-to-json
  && curl -fsSL -O https://github.com/dhall-lang/dhall-haskell/releases/download/1.21.0/dhall-json-1.2.7-x86_64-linux.tar.bz2 \
  && tar xvjf dhall-json-1.2.7-x86_64-linux.tar.bz2 && mv bin/dhall-to-json /usr/local/bin/dhall-to-json \
  && chmod +x /usr/local/bin/dhall-to-json \
  # Install psc-package
  && curl -fsSL -O https://github.com/purescript/psc-package/releases/download/v0.5.1/linux64.tar.gz \
  && tar xvzf linux64.tar.gz && mv psc-package/psc-package /usr/local/bin/psc-package \
  && chmod +x /usr/local/bin/psc-package \
  # Install purs
  && curl -fsSL -O https://github.com/purescript/purescript/releases/download/v0.12.3/linux64.tar.gz \
  && tar xvzf linux64.tar.gz && mv purescript/purs /usr/local/bin/purs \
  && chmod +x /usr/local/bin/purs

USER node

WORKDIR $HOME/app

作業

とりあえず、package-setsをforkしてcloneしましょう。 package-setsは、dhallという言語で管理されているのでdhallを書くわけですが、知らなくても大丈夫です。 他のファイルを見れば書き方はわかります。src以下にもぐって、マネッコして書きましょう。

さて、書き終わったら、make setupを叩きます。 これで、packages.jsonってのが更新されてdiffにでてくるはずなので、中身を見て、自分が足したpackageが間違いなく足されているのかを確認しましょう。

そのあとは、自分の追加したpackageをverifyします。

たとえば、あなたがpackage-setsにeternal-force-blizzardというパッケージを追加したのであれば、psc-package verify eternal-force-blizzardと打ちましょう。

依存をインストールしてコンパイルを行います。無事に終了すればOKです。

まぁよくわかんなかったら、コントリビューションガイドに親切に書いてあるので見てみましょう。

PR

ここまでできたらあとはPRを送るだけです。今回は私もはじめてだったので一応「俺のライブラリ入れていい?」って感じでPRを送りました。 また、他人のパッケージもひとつ追加する必要があったのでそれも追加しました。たぶん他人のライブラリも前提条件を満たしていてverifyが通れば追加して問題なさそうです。実際に私のパッケージも既に一つ存在していました。

以下は私が送ったPRです。dhallの編集の仕方がわかんなかったら参考にしてください。

github.com

一度に大量に追加したのでかなり疲れました...。

夜中にPR送ったのですが、起きたらマージされていて、新しいpackage-setsがリリースされてました!ありがたい!!

とまぁ、ちょっとめんどくさいんですけど、簡単です!

宣伝

最近、ちゃんと業務で使えるものを目指して、PureScript向けのUIライブラリをつくりました。

purescript-freedomと言います。

npm dependencyはありません。

このパッケージは既存のパッケージで使いたいと思うものがなかったことが原因でつくったため、 既存のUIパッケージとの差別化がされています。

ガイドやサンプルプロジェクトも用意していて、すでに派生パッケージもいくつか公開しています。

ElmからPureScriptに興味を持った人、HalogenやPux、react-basicになにかしら気に入らないことがある人、「あぁ?なんだどういうもんかみてやろうじゃねえか?」という人、騙されたと思って見てくれると嬉しいです。

ホントに騙しちゃったらごめんなさい、許してね♡

2020/07/07 追記

新たにUIライブラリ、purescript-grainをリリースしました。 チューニングもしてあるのでそちらのほうがおすすめです。

purescript-bucketchainの紹介

はじめに

rackやexpress, koaのようなウェブサーバーのための共通のインターフェースを提供するライブラリをつくりました。 それがpurescript-bucketchainです。

背景

外部のnpm packageに頼らず、ランタイムがnodejsなのでパイプで上手くやってくれるようにストリームベースなインターフェースで、koaとかexpressっぽさを踏襲しつつ、ミドルウェアを積みまくってサーバーを構築できるような基盤がほしくなりました。

使い方の流れ

  1. ミドルウェアを用意
  2. ミドルウェアを使ってサーバー作成
  3. サーバー起動

ミドルウェアを用意する

特定のpathで特定の画像を返すミドルウェアをつくってみましょう。 まずは以下を見てください。

middleware :: Middleware
middleware next = do
  http <- ask
  if requestMethod http == "GET" && requestURL http == "/img"
    then liftEffect do
      setStatusCode http 200
      setHeader http "Content-Type" "image/png"
      Just <<< fromReadable <$> createReadStream "example/300x300.png"
    else next

MiddlewareHandler (Maybe ResponseBody) -> Handler (Maybe ResponseBody)の型シノニムです。 つまりnextHandler (Maybe ResponseBody)です。 middleware1 <<< middleware2のような形でミドルウェアを結合して組み合わせて一つのミドルウェアをつくります。

ResponseBodyはその名の通りレスポンスのボディですが、実態はReadable Streamとなっており、Readable StreamであればResponseBodyに変換可能です。また、StringからResponseBodyをつくることも可能です。

Maybe ResponseBodyとなっていますが、Nothingの場合だとボディ無しでレスポンスを返します。

当然nextを呼び出さなければ次のミドルウェアの処理には移りません。

Handlerモナドは、非同期な処理が中心のnodejsで、ミドルウェアがシーケンシャルに呼び出されることを保証するため、Affをベースにしています。また、nodejsはrequest streamとresponse streamを1リクエストで扱いますが、それらを1つにまとめたHttp型をもっています。 早い話、具体的な型定義はnewtype Handler a = Handler (ReaderT Http Aff a)となっています。

サーバーを作成

まずはこちらを見てください。

server :: Effect Server
server = createServer middleware

さて、MiddlewareHandler (Maybe ResponseBody) -> Handler (Maybe ResponseBody)でした。最後のnextには何が渡されるのでしょうか? createServerの処理の中で、最後のnextには、404を返すだけのHandlerが渡されます。 つまり、どのミドルウェアの処理にも引っかからないと404を返すようになっています。

サーバーを起動

最後はさっきのサーバーを起動するだけでサーバー出来上がりです! かなり簡単ですね。

main :: Effect Unit
main = server >>= listen opts

opts :: ListenOptions
opts =
  { hostname: "127.0.0.1"
  , port: 3000
  , backlog: Nothing
  }

詳しくはサンプルコードを見るとよりわかりやすいかもしれません。

既存のミドルウェア

Bucketchain · GitHub

こちらに現在8個のミドルウェアができています。 Bucketchainはあくまでインターフェースで、これに則ってミドルウェアとしてライブラリやフレームワークをつくるという世界観なので、より具体例を見るのであれば、こちらをご参照ください。

久しぶりにPureScriptのパッケージを書いたりしてた

追記 本記事のconveyor向けパッケージはメンテされていません。ご注意ください。

しばらくPureScriptから離れていたが、 サーバー用にいくつかほしくなった仕組みがあったため、久しぶりにPureScript書いた。

Update

New

とりあえずライブラリを書く時はまだしばらくbowerを使っておくことにした。

そんなにPureScript忘れてなかった。

久しぶりにPursuit見たらサーバー向けっぽいパッケージが増えてた。

Pursuitに最新版があがってないパッケージがあった。node系のやつだったかな。

PureScriptは0.12以降であんまり動きがないように見えてたけど、マイルストーンを見た感じ、1.0に向けて頑張ってるっぽいですね。

parcel-plugin-build-hooksつくった

parcelで任意のビルドイベント(例えば、ビルド完了時、ビルド開始時など)でcliを叩く、などをやりたい。 この場合、parcelの生APIを用いれば上記の目的は達成できるが、これはparcelの生APIを使ってビルドスクリプトを書くことを意味する。 なるべくビルドスクリプトは用意したくない。

こういった気持ちが生まれたため、プラグインにしときました。

parcel-plugin-build-hooks

使い方

package.jsonに任意のイベント時に叩きたいコマンドを書く。

{
  "name": "your-app",
  "main": "index.js",
  "dependencies": {
    "parcel-plugin-build-hooks": "^1.0.0"
  },
  "parcel-plugin-build-hooks": {
    "buildStart": "// Do something",
    "bundled": "// Do something",
    "buildEnd": "// Do something",
    "buildError": "// Do something"
  }
}

以上。

フリーランスになって1年経った

気づいたら、フリーランスになってから1年経っていました。 社会に出る前からフリーランスになる可能性は高そうだなと思っていたものの、踏ん切りがつかなかった自分が1年フリーランスできたことに対して、昔の自分は驚く気がします。

フリーランスになって

フリーランスになる前は以下について心配していました。

  1. 実力的な意味で自分がやっていけるのか
  2. お金の交渉をできるのか
  3. 仕事は取ってこれるのか
  4. 限られた時間で成果を出せるのか

1については、厳密には他人が決めることなので正確なことは言えません。ですが、話している感触としてはやっていけていると勝手に思っています。 他のフリーランスを見ていると、やっていきかたは人によって様々で、得意分野に特化して技術的な意味で役に立っているパターンもあれば、普通に機能開発を手伝っているパターンもあります。他人がしたことで社員が喜んでいるのを見たりして、「こういうのが喜ぶのか。喜ばれる目的に対してコスパがいいので記憶に残しておこう。」など、うまくやっている人を見て私も学んでいます。フリーランスの方は持ってるスキルも色々な意味で様々なんですが、スキルが原因でクビにされてるのは「今のところ」は見たことがないです。

2については、自分の場合は意外と出来ました。出来たというよりは、堂々とほしい金額を言っただけで払ってもらえている、といったところです。 わたしはこの1年のうちに3社経験していて(いずれも準委任です)、その中には金がある企業全然ない企業両方含まれています。 その前提で、すべて言い値で通りました。 ほしい金額を言うことがまず大切だなということを痛感しています。

3については、仕事を取りに行かずに済んでいる、という状況です。私はサラリーマンとして3社経験しています。そしてその3社で知り合った方々も色々なところへ散っていってます。私はサラリーマン時代に真面目に働いていたおかげかこの状況下なおかげか知り合い経由でお誘いが断続的に来ます。中にはお誘いを断っても定期的に移る気がないか確認してくる人もありがたいことに存在しています。 知り合いが重要というのは本当でした。

4については、自分の中ではまだ最大限ではないなと思っています。 まず業務時間については、サラリーマンで言うところの残業はそのまま損失なので、現在では、それを1mmもやらないようにし、業務してるとき以外は業務に関することをなるべく考えないように徹するようになりました。 また、今は2社から仕事を受けていますが、業務内容もまるっきし違いますし、1週間のうちに業務が切り替わるのもすごいコンテキストスイッチが発生します。 これらの前提、且つ、単位時間を1週間とみなした場合、サラリーマンとやっていたときと比べて単位時間あたりのパフォーマンスが落ちている気はしています。

不安はある

やはり不安なのは、いずれ誘ってくれる人がいなくなったらどうしようというのは常に頭にあります。 なので、1社1社、誠実に働かないといけないなとたまに考えたりします。 いつまでできるのかはわからないですが、どこかに入社したいなと思うまではフリーランスを続けようと思います。

Incremental lambda calculusとUIへの応用

はじめに

1ヶ月くらい前にpurescript-purviewというUIライブラリがリリースされました。 このライブラリは既存の仮想DOMに代わるような位置づけのライブラリとして実験的にリリースされたUIライブラリとされています。 何が実験的なのかというと、Incremental lambda calculusという概念に基づいて実装されているということでした。 一体何なんだこれは?と思ったので調べることにしました。

Incremental lambda calculus

ひとまずググってみたところ、こちらのサイトが出てきました。 上記のサイト内に論文のpdfへのリンクが貼ってありますが、「なるほどわからん」という感じです。 データベース技術を参考にした考え方っぽいことはわかりました。

さっぱりわからなかったときは実装を見たほうが早いということで、この考え方に基づいたライブラリを読むことにしました。 それがpurescript-incremental-functionsです。 purescript-purviewはこのライブラリをベースに実装されています。

コアとなる型クラスのドキュメントはこちらになります。

このソースコードから読み取れることは、「あるデータ型に対する操作を、変更増分で表現し、段階的に適用する」、というような考え方のようです。

例えば、こちらのライブラリで提供されているArrayの増分計算版であるIArray型の操作はInsertAt, DeleteAt, ModifyAtで表現するような形になっています。

次はこの考え方をどうUIに応用していくのか見ていきます。

UIへの応用

まず、前提として、purviewはこれまでの仮想DOMと同じように、状態(Modelという名前で扱われています)を受け取ってUIに変換します。そして、その状態への変更は、purescript-incremental-functionsを用いて定義された変更増分を用いて更新します。 また、purescript-incremental-functionsで提供されている型でJetというものがあります。こちらは計算対象の型と変更増分を両方もったデータ表現です。 purviewではあらゆるものをJetで表現します。purviewで提供されているUIの型もJetで記述していくような形になっています。

では、読んでいきましょう。 読むメインの場所はこのあたりになります。

まず、全体的な世界観としては、Jetで表現されたModelからJetで表現された仮想のUIのマッピングとなっていて、この関数にComponentという名前がついています。 ここで増分計算について考えます。Modelを変更したいので増分を与えるわけですが、先のComponentに現在のModelと、与えられた増分を用いたJetを渡します。 JetからJetのマッピングは増分もマッピングしてくれるため、Modelの増分から仮想のUIの増分が導きだせます。 これらのUI増分をpatchとして、現在の仮想のUIとそのpatchから生DOMに変更を与えていきます。 そして、次の計算に備えて、それぞれの増分から新しいModelと新しい仮想のUIを導き、referenceに保持し直して、1サイクル終了となります。

面白いところ

仮想DOMを読んだり実装したことがあればわかるのですが、現在の仮想DOMと新しい仮想DOMの差分計算の実装を必ず書くことになります。 よくあるkeyを与えたりするのは子要素の差分計算の効率のためです。  そして、現在の仮想DOMと新しい仮想DOMを直接比較しながら、あるいはその比較から差分表現を導き出して、生DOMに変更を適用していくような形になります。 この差分計算は通常かなり泥臭い実装になると思っています。

purviewの場合は、データの操作を増分で表現してしまい、その増分とユーザーが自ら定義した仮想のUI関数を利用して、UIの変更増分を直接導き出してしまいます。 このあたりの実装はとてもパズル的で面白く、非常にエレガントなやり方に見えました。(小並感)

一方で、増分適用できるデータ型を用意する必要がありますし、 データの変更を全て増分で与えなければいけないのも場合によっては面倒なケースがある気がしています。 例えば、IArrayを全て逆順にしたいときは全部DeleteAtしてから逆順にInsertAtするというような形でしょうか。あるいはAtomic型でくるんでまるごと置き換えるという形でしょうか。 いずれにせよ、増分の与え方は脳死状態ではできないケースがあるような気はしました。 なので、ヘルパー関数が充実していないと結構つらいケースがでてくるかもしれません。

さいごに

生DOMへの変更適用でこんな面白いやり方があるのかという発見がありました。 まだ出たばかりなのでバグがあるかもしれませんが、非常に勉強になるライブラリだと感じました。

日記

振込用紙

私はフリーランスプログラマをやっていて、定期的に国民年金保険料の振込用紙が送られてきます。

封を開けるたびに「いつまで俺はフリーランスやってるだろう?」と無駄に考えます。

このように考える要因はもしかしたらどこかでフリーランスをやめるってことも想定しているからだと思います。

なので、基本的には自分の求める条件に合う場所が万が一あれば就職したいのかもしれません。(が、おそらくありません。)

怠けるループ

たまに何もしたくなくなる時期が襲ってきます。

そのような時期に突入したときになるべく早く脱出する方法を考えていました。

洗い物や掃除(特に水回りなど)は、終わったあとがピカピカで気持ちがよく、不思議と活動意欲が湧いてくるので、地味だけど効果がありそうです。前頭葉に刺激うんぬんみたいな理屈を見たことがあるようなないような。まぁそのあたりのことはどうでもいいです。

この記事

この記事はなんの意味もありません。 作業で行き詰まったところ、脳みそを休ませるつもりで書いてみました。