名指しでやれ

自分のアウトプットを人にチェックしてほしいとき、というのは誰にでもあるし、仕事をしていれば日常的に観測できる事象である。

私はプログラマをしているが、プログラマの場合であれば、例えば、本番のデータを修正するスクリプトを他人にチェックしてもらうとか、単純にPullRequestをレビューしてもらう等だ。

この時に、不特定多数に対してチェックを呼びかけるというケースを観測することがある。 例えば、slackでのやりとりであれば、@channel等の不特定多数にたいするメンションのみを用いたやりとり、Pull Requestであれば、複数の人間をレビュアーに設定する、等である。

こういった不特定多数にチェックを求める行為をする癖がある人がいたらすぐにやめたほうがいい。

理由は明確で、チェックしないで無視する、あるいは、適当にチェックしてレスを返すという流れを生む確率があがるし、実際みんな基本無視で、レスをしていてもめっちゃ適当、という事例を見ているからだ。

他の人が見てる、自分が返さなくていいという前提になってしまうので、無視しやすいし、無視しなかったとしても真剣さのないチェックになって適当にレスを返しやすい。

きちんとチェックをしてもらいたい意思があるなら、多数にお願いをすること自体はいいが、それだけでは絶対に駄目で、必ず生贄を一人だけ指名する必要がある。「あなたがメインのレビュアーです」「あなたがチェックしないなら先に進みません」ということが相手に伝わらないといけない。

チェックをお願いされた側は、直接指名は自分一人だけなので「ちゃんとしなきゃ」と考えやすくなる、少なくとも無視はされなくなる。

人にやってほしいチェックがあったら、必ず名指しで生贄を捧げろ。もちろん生贄はちゃんとチェックしてくれそうな人、できそうな人を選びましょう。

RecoilにインスパイアされてPureScript用仮想DOMつくった

追記

ここに記述した内容は既に古くなっています。 内部実装は既に書き直されていますし、Hooks likeなAPIも少しだけインターフェースが変更されています。 興味のある方は直接purescript-grainへ見に行ってください。

動機

Recoilが思ったより話題になっていたので、PureScriptだったらどういう表現ができるかなと思ってつくりたくなった。

そういうわけで、PureScriptにおける仮想DOM実装の3作目が爆誕してしまった。

結果

purescript-grain

こういう感じになった

型クラスのインスタンスにした型が各状態になり各状態キーにもなるという感じになった。 実はここの表現に関してはかなり迷って、最初は、シンボル(型レベル文字列)と状態の型をもち、キーの型から状態の型への関数従属性をもった型クラスのインスタンスにすることで表現しようと考えたが、PureScriptはOrphan instance禁止なため、シンボルはどんなシンボルでもシンボルというひとつの型、且つ、上記の関数従属性があるので、型クラスの宣言と別のモジュールでインスタンスの宣言をするとコンパイルが通らない。 よって、そこから少し右往左往してこの形に落ち着いた。

グローバルステート

以下のように任意の型をGlobalGrainインスタンスにし、状態の取得・購読や更新関数の取得をプロキシ経由で行う。

その型のコンストラクタ関数の参照を型の参照とみなし、状態のキーとして扱い、一意に識別する。

各所で勝手に宣言された状態は、ひとつのオブジェクトにおしこまれるようになっているため、全状態を見たりできるdev toolを作ろうと思えばつくれる状態にはなっている。

ただ、現状は型の参照をキーにして、uuid v4ベースの文字列を管理し、その文字列を内部で状態のキーとして使っているため、ヒューマンリーダブルではない。

これに対するアプローチは状態名のヒューマンリーダブルな表現を登録してもらうか、あるいは、型からモジュール名を含めたシンボル(型レベル文字列)をつくる、みたいな、コンパイラが解決する特殊な型クラスがあったりするといい。

後者の場合は、以下のtypeRefOfの実装もしなくて済むようになる。 後者の案に似たような提案はされているのでそういった機能が入ることを0.1%くらい期待しているが、あまり期待できない。

前者の案を採用する場合は、所詮表示用なのでユニークである制約はつける必要はないが、まぁだるいといえばだるいので、結局、現状はヒューマンリーダブルな名付けはしなくていいようになっている。この課題に関しては先延ばしにするということにした。

import Prelude

import Grain (class GlobalGrain, GProxy(..), VNode, fromConstructor, useUpdater, useValue)
import Grain.Markup as H

newtype Count = Count Int

instance globalGrainCount :: GlobalGrain Count where
  initialState _ = pure $ Count 0
  typeRefOf _ = fromConstructor Count

view :: VNode
view = H.component do
  Count count <- useValue (GProxy :: _ Count)
  updateCount <- useUpdater (GProxy :: _ Count)
  let increment = updateCount (\(Count c) -> Count $ c + 1)
  pure $ H.div
    # H.onClick (const increment)
    # H.kids [ H.text $ show count ]

動的なアイテムに対するグローバルステート

なにか動的なアイテムにそれぞれに対して、状態をつくりたい、要するにUIへの通知範囲を極端にしぼりたいケースにも対応している。

以下は簡単な例である。 この場合は、プロキシの値がもつキーと型への参照を用いてアイテムごとの状態キーが作られるようになっている。

import Prelude

import Grain (class KeyedGlobalGrain, KGProxy(..), VNode, fromConstructor, useUpdater, useValue)
import Grain.Markup as H

newtype Item = Item
  { name :: String
  , clicked :: Boolean
  }

instance keyedGlobalGrainItem :: KeyedGlobalGrain Item where
  initialState (KGProxy key) = pure $ Item
    { name: "Item " <> key
    , clicked: false
    }
  typeRefOf _ = fromConstructor Item

view :: String -> VNode
view key = H.component do
  Item item <- useValue (KGProxy key :: _ Item)
  updateItem <- useUpdater (KGProxy key :: _ Item)
  let onClick = updateItem (\(Item i) -> Item $ i { clicked = true })
  pure $ H.div
    # H.onClick (const onClick)
    # H.kids [ H.text $ item.name <> if item.clicked then " clicked" else "" ]

その他

この記事の本筋とは関係ないので書かないが、ローカルステートもある。 また、FFIでnpmパッケージを使っておらず全体的にPureScriptで書いてある。

また、2作目につくった仮想DOMは、でかいひとつのグローバルステートしかもてない制約と一部グローバルステートしか持てないことで不便になるケースに対応するための仕組みをいれた形のものだが、多数のファイルが型レベルでグローバルステートに依存する設計になっており、ステートに手を加えると、大量のファイルが再コンパイルされるので、 コードベースがでかくなってくると差分ビルドしてても重い。 というかそういう状況が発生し始めていた。

3作目のpurescript-grainは各グローバルステートの宣言をモジュールに分けておくだけで、仕組み上同じ問題は起こらない。

また、仕組み上、本家のReact hooksと違い、呼び出し順序が変わろうが、呼び出したり呼び出さなかったりしようが、バグらない。

既に少し派生パッケージをつくってみたが、書き心地としては悪くない。(自分が作ったので自分がそう思うのは当たり前だが...

なお、package-setsに追加したものの、まだpackage-setsがリリースされてないし、今日もさらにアップデートしたので、 試したくなった場合は、packages.dhallのadditionに手動で加えてもらう必要が有ります

新年が始まったわけだが

2019年

まぁホントに色々やったんだけど、ギョームとしては、WebRTC沼に参戦したこととPureScript及び俺製VirtualDOMのpurescript-freedomをぶちこんだこととか 、AWSからGCPのインフラ移行やったのとかログ基盤作ったりあたりがトピックかな。 今年はわりと大げさめなタスク多かったかも。 WebRTC一本で飯食える人がいる意味がよくわかった。商用SFUさまさまです。

プライベートは二人の子供に振り回されててんやわんやしてた。一日のうちに何に体力を使うかの配分について改善のしようがある。

家を買おうとして9000万の家でも買えるとファイナンシャルプランナーに言われたが、自分で正確なライフプラン表つくったら絶対に買えないレベルだったのでファイナンシャルプランナーの言うことを鵜呑みにしてはいけない、というかあいつらもただソフトに適当に数字打ち込んで御託並べてるだけなので信用してはいけない。

2020年

今年は自分のサービスで少しでも売上を立てる年にする。 歳をとって待つのが得意になったので長い目で見ながらやっていく。

家は買いたいけど新築はやっぱり無駄に高い。中古を狙っている。 家持ってる人で家売りたい人は、twitterの@oreshinyaまで連絡ください。買いたいエリアだったら検討します。

PureScriptのJWT用のライブラリを作りました

PureScriptのJWT用ライブラリ、purescript-simple-jwtを作りました。

一応既存のものでpurescript-jwtというライブラリがありましたが、デコード専用、且つ、署名の検証もしないものだったので、自分で作ることにしました。

使い方

エンコード

encode :: forall payload. WriteForeign payload => Secret -> Algorithm -> payload -> Effect Jwt

シークレットとアルゴリズムペイロードを渡します。 アルゴリズムついては、HS256とHS512に対応しています。 ペイロードについては、仕様上不定なので、まぁ適当にレコード渡してください。

デコード

decode :: forall payload. ReadForeign payload => Secret -> Jwt -> Effect (Either JwtError payload)

シークレットとJWTを渡します。検証時に用いるアルゴリズムはJWTのヘッダから勝手に選びます。 トークンがおかしかったり、検証に失敗したりするとエラーが返ってきます。 ペイロードの中身を使ったチェック(例えば、exp等による有効期限チェック)については、このライブラリの責務の範囲外としています。

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に向けて頑張ってるっぽいですね。