[Web フロントエンド] Elm に心折れ Mint に癒しを求める

2020/03/19
このエントリーをはてなブックマークに追加

エルムの木

こんにちは、 Elm

Elm は関数型の小さな言語仕様を持つ altJS です。 Redux が影響を強く受けた ことでも有名ですね。 “No Runtime Exceptions” は魅力的ですし、何より興味深いのは The Elm Architecture です。

Redux にはあまりセンスを感じられなかったのですが、 The Elm Architecture はとても良さげに見えました。 Elm が持つ 代数的データ型パターンマッチ、状態の不変性、 レコードの部分更新式 などとうまく馴染み、簡潔に書きやすそうで、なにより読みやすそうだと思いました。

コード例(雰囲気つかみ用)

https://guide.elm-lang.jp/architecture/forms.html より抜粋

-- MODEL

type alias Model =
  { name : String
  , password : String
  , passwordAgain : String
  }


-- UPDATE

type Msg
  = Name String
  | Password String
  | PasswordAgain String


update : Msg -> Model -> Model
update msg model =
  case msg of
    Name name ->
      { model | name = name }

    Password password ->
      { model | password = password }

    PasswordAgain password ->
      { model | passwordAgain = password }

またね、 Elm

それで試しに小さなアプリを書いてみようとしたのですが、恥ずかしながら手も足も出ませんでした。

私が Elm 初日につまづいたところを JavaScript で素直に書くと次のようになります。

element.addEventListener('pointerdown', event => {
  if (event.button !== 0) {
    return
  }
  event.currentTarget.setPointerCapture(event.pointerId)

  // event.clientX, event.clientY を使ってほげほげ....
})

elm-pointer-events パッケージ の README によれば、 pointer capture を使うには ポート を使う必要があるようです。 Elm からの要求を受け付けて setPointerCapture() を行う「ポート」を JavaScript 側で用意 して、 Elm から呼び出す、という実装になります。

ポートはアプリ専用(パッケージはポート使用禁止)なので、アプリごとに setPointerCapture() のポートを記述しなければなりません。

ニッチな API ならともかく PointerEvents でこの状況となると、普通にアプリを書こうとしたらポートだらけになるのでは… と想像して心が折れた私はいったん Elm と距離を置くことにしました(そもそも接近できてない)。

Elm のロードマップの Where is the localStorage package? というセクションには、 SPA 機能のあとには Web プラットフォーム のサポート拡大が最優先事項になる旨、さらには、 Web プラットフォームのサポート拡大のあとで Elm に戻ってくるのはアリだよ的なことまで書かれています。

そっか、じゃあ、またね、 Elm。

そんな折り、偶然見かけたのが Mint でした。

こんにちは、 Mint

ほら貝を吹く看護師

ミントさんの代名詞 ナースとピコハン

Mint はシングルページアプリケーションを書くためのプログラミング言語を謳っています。私はアンチ SPA 派(ページごとに分けて実装する方が圧倒的にメンテナンスしやすいと信じているひと、 Paint Holding があれば尚更)なのでこの肩書きには魅力を感じないのですが、ドキュメントを眺めてみると Mint には注目する価値がありそうだと思いました。

特徴

コンポーネントのコード例(雰囲気つかみ用)

component Article.Comments {
  connect Stores.Comments exposing { status }

  get content : Html {
    if (Array.isEmpty(comments)) {
      <div>
        "This article does not have any comments yet."
      </div>
    } else {
      <div>
        for (comment of comments) {
          <Article.Comment comment={comment}/>
        }
      </div>
    }
  } where {
    comments =
      case (status) {
        Api.Status::Ok comments => comments
        => []
      }
  }

  fun render : Html {
    <Status
      message="There was an error loading the comments."
      loadingMessage="Loading comments..."
      status={Api.toStatus(status)}>

      <{ content }>

    </Status>
  }
}

Mint に触れてみた

以前からほしかった自分用の小さなアプリを Mint で書いてみています。

ほしかったのは、アイコンセットを補うためのアイコンデザインツールです。 Font AwesomeMaterial Design Icons を使っていて、ほしいアイコンがどうしても見つからないとき。自分で描けるものなら描きたいけど、高機能なデザインツールとか使いこなせないし、ベジェ曲線とか思うように描けないし… 直線と円弧だけのデザインツールがあったらどうだろう…? 的な感じで。

setPointerCapture() は標準ライブラリに含まれていなかったものの、インライン JavaScript (バッククォートで囲まれた JavaScript の式)で普通に書けました。

  fun onPointerDown (event : Html.Event) : Promise(Never, Void) {
    if (event.button == 0) {
      sequence {
        `#{event.currentTarget}.setPointerCapture(#{event}.event.pointerId)`

        /* event.clientX, event.clientY を使ってほげほげ.... */
      }
    } else {
      Promise.never()
    }
  }

  fun render : Html {
    <svg onPointerDown={onPointerDown}>
      /* ... */
    </svg>
  }

Mint に触れてみて

我々プログラマというものは、言語の好き嫌いについては一風変わった習性を持っています。 本屋で「プログラミング言語XYZ」といった本を見かけても、「わかってるよ、30秒もあれば、XYZで気に入らない所が何か見つかるさ」と反射的に思ってしまうのです。 プログラミング言語をマスターするまでには多大な時間と手間がかかり、その達成感も不確かな形で遅れてやってきます。 そのような努力をしない理由をさっさと見つけることが生存本能となっています。 つまり、そのような努力は賭け金の高い危険な投資であるので、否定的な決断を早目に下せば大いに安心できるというわけです。

The Case for D
http://dusers.dip.jp/modules/wiki/?Articles%2FThe%20Case%20for%20D%2F1

ということで、 Mint に 2 日間ほど触れてみて気になった点を挙げます。

  • コンポーネントやレコード型などのスコープがファイルで区切られず、ファイルを越えて何でも見えてしまう
  • 型推論がない
    • たとえば Maybe.map() を使おうと思うと
      • こう書きたいのに
        value
        |> Maybe.map(\x -> x + 1)
      • 型推論がないので
        value
        |> Maybe.map((x : Number) : Number { x + 1 })
      • ECMAScript には Optional Chaining 版パイプライン演算子 ?|> or ?> の提案があったりする
        Optional chaining support for the pipeline operator? · Issue #159 · tc39/proposal-pipeline-operator
        https://github.com/tc39/proposal-pipeline-operator/issues/159

        value
        ?> x => x + 1
    • 戻り値の型推論は(もうすぐ?)入りそう、引数の型推論は(まだ?)入らなそう
  • コンポーネントのプロパティは必須にできず、デフォルト値が必須
    • プロパティ渡し忘れバグの発見が遅れる
    • 複雑なレコード型だとデフォルト値を書くのがつらい、かといって Maybe にしたくない
    • 現在審議(投票)中…
  • Elm と同様、やはり DOM などの Web プラットフォーム API が弱い
    • Elm と違って、 JavaScript の式がその場に書けるのでなんとでもなるっちゃなる、ただ型システムに守ってもらえなくなる
    • README の Ways you can contribute に “create modules for not yet implemented Web APIs” って書かれてるし、これからって感じ
    • TypeScript の lib.dom.d.ts をなんかいい感じにごにょごにょしたら使えたりしないものかね?(適当)
  • Mint が知ってる CSS プロパティ 以外のプロパティを style ブロックに記述するとコンパイルエラーになる
    • touch-action とか stroke-opacity とか普通に書こうとして引っかかった
    • チェック自体はありがたいけどチェックを回避する手段もほしい
    • インラインスタイル(仮想 DOM の style 属性)は文字列で自由に書けるのでどうにかできるにはできるけど
  • コンパイルエラーがコマンドライン上に表示されずブラウザー上に表示される
    • VSCode 上でジャンプできないの地味に不便
  • VSCode Extension がぜんぜん助けてくれない

まとめ

プログラミング言語 Mint を紹介しました。

特徴として挙げたメリットは期待通りでした。開発環境整備やライブラリ選定に悩むことはなく、書き味は良く、ビルドは爆速です。気になる点もたくさん書き連ねましたが、まだ成熟していないだけで、多くは時間が(実際には中の人の努力が)解決してくれそうな気がしています。型推論を入れてエディタープラグインをちゃんとするだけでもだいぶ使えるだろうと想像します。

いますぐ使うことをおすすめするつもりはありませんが、今度どこかで Mint の記事やニュースを見かけたときには興味を持って読んでもらえたらいいな、と思います。

null でした。


2020/04/07 追記

記事執筆時に利用したバージョンはそれぞれ

  • Elm 0.19.1
  • Mint 0.8.0

です。情報が古くなっている可能性がありますのでご注意ください。特に Mint は言語仕様もエコシステムも発展途上であり変化が速いです。 0.9.0 では型推論やインライン JavaScript の型指定、コンポーネントの必須プロパティなどが追加されています。

その他の記事

Other Articles

2020/02/28
さようなら、TypeScript enum

2020/02/14
受付のLooking Glassに加えたひと工夫

2020/01/28
カブクエンジニア開発合宿に行ってきました 2020冬

2020/01/30
Renovateで依存ライブラリをリノベーションしよう 〜 Bitbucket編 〜

2019/12/27
Cloud Tasks でも deferred ライブラリが使いたい

2019/12/25
*, ::before, ::after { flex: none; }

2019/12/21
Top-level awaitとDual Package Hazard

2019/12/20
Three.jsからWebGLまで行きて帰りし物語

2019/12/18
Three.jsに入門+手を検出してAR.jsと組み合わせてみた

2019/12/04
WebXR AR Paint その2

2019/11/06
GraphQLの入門書を翻訳しました

2019/09/20
Kabuku Connect 即時見積機能のバックエンド開発

2019/08/14
Maker Faire Tokyo 2019でARゲームを出展しました

2019/07/25
夏休みだョ!WebAssembly Proposal全員集合!!

2019/07/08
鵜呑みにしないで! —— 書籍『クリーンアーキテクチャ』所感 ≪null 篇≫

2019/07/03
W3C Workshop on Web Games参加レポート

2019/06/28
TypeScriptでObject.assign()に正しい型をつける

2019/06/25
カブクエンジニア開発合宿に行ってきました 2019夏

2019/06/21
Hola! KubeCon Europe 2019の参加レポート

2019/06/19
Clean Resume きれいな環境できれいな履歴書を作成する

2019/05/20
[Web フロントエンド] 状態更新ロジックをフレームワークから独立させる

2019/04/16
C++のenable_shared_from_thisを使う

2019/04/12
OpenAPI 3 ファーストな Web アプリケーション開発(Python で API 編)

2019/04/08
WebGLでレイマーチングを使ったCSGを実現する

2019/04/02
『エンジニア採用最前線』に感化されて2週間でエンジニア主導の求人票更新フローを構築した話

2019/03/29
その1 Jetson TX2でk3s(枯山水)を動かしてみた

2019/03/27
任意のブラウザ上でJestで書いたテストを実行する

2019/02/08
TypeScript で “radian” と “degree” を間違えないようにする

2019/02/05
Python3でGoogle Cloud ML Engineをローカルで動作する方法

2019/01/18
SIGGRAPH Asia 2018 参加レポート

2019/01/08
お正月だョ!ECMAScript Proposal全員集合!!

2019/01/08
カブクエンジニア開発合宿に行ってきました 2018秋

2018/12/25
OpenAPI 3 ファーストな Web アプリケーション開発(環境編)

2018/12/23
いまMLKitカスタムモデル(TF Lite)は使えるのか

2018/12/21
[IoT] Docker on JetsonでMQTTを使ってCloud IoT Coreと通信する

2018/12/11
TypeScriptで実現する型安全な多言語対応(Angularを例に)

2018/12/05
GASでCompute Engineの時間に応じた自動停止/起動ツールを作成する 〜GASで簡単に好きなGoogle APIを叩く方法〜

2018/12/02
single quotes な Black を vendoring して packaging

2018/11/14
3次元データに2次元データの深層学習の技術(Inception V3, ResNet)を適用

2018/11/04
Node Knockout 2018 に参戦しました

2018/10/24
SIGGRAPH 2018参加レポート-後編(VR/AR)

2018/10/11
Angular 4アプリケーションをAngular 6に移行する

2018/10/05
SIGGRAPH 2018参加レポート-特別編(VR@50)

2018/10/03
Three.jsでVRしたい

2018/10/02
SIGGRAPH 2018参加レポート-前編

2018/09/27
ズーム可能なSVGを実装する方法の解説

2018/09/25
Kerasを用いた複数入力モデル精度向上のためのTips

2018/09/21
競技プログラミングの勉強会を開催している話

2018/09/19
Ladder Netwoksによる半教師あり学習

2018/08/10
「Maker Faire Tokyo 2018」に出展しました

2018/08/02
Kerasを用いた複数時系列データを1つの深層学習モデルで学習させる方法

2018/07/26
Apollo GraphQLでWebサービスを開発してわかったこと

2018/07/19
【深層学習】時系列データに対する1次元畳み込み層の出力を可視化

2018/07/11
きたない requirements.txt から Pipenv への移行

2018/06/26
CSS Houdiniを味見する

2018/06/25
不確実性を考慮した時系列データ予測

2018/06/20
Google Colaboratory を自分のマシンで走らせる

2018/06/18
Go言語でWebAssembly

2018/06/15
カブクエンジニア開発合宿に行ってきました 2018春

2018/06/08
2018 年の tree shaking

2018/06/07
隠れマルコフモデル 入門

2018/05/30
DASKによる探索的データ分析(EDA)

2018/05/10
TensorFlowをソースからビルドする方法とその効果

2018/04/23
EGLとOpenGLを使用するコードのビルド方法〜libGLからlibOpenGLへ

2018/04/23
技術書典4にサークル参加してきました

2018/04/13
Python で Cura をバッチ実行するためには

2018/04/04
ARCoreで3Dプリント風エフェクトを実現する〜呪文による積層造形映像制作の舞台裏〜

2018/04/02
深層学習を用いた時系列データにおける異常検知

2018/04/01
音声ユーザーインターフェースを用いた新方式積層造形装置の提案

2018/03/31
Container builderでコンテナイメージをBuildしてSlackで結果を受け取る開発スタイルが捗る

2018/03/23
ngUpgrade を使って AngularJS から Angular に移行

2018/03/14
Three.jsのパフォーマンスTips

2018/02/14
C++17の新機能を試す〜その1「3次元版hypot」

2018/01/17
時系列データにおける異常検知

2018/01/11
異常検知の基礎

2018/01/09
three.ar.jsを使ったスマホAR入門

2017/12/17
Python OpenAPIライブラリ bravado-core の発展的な使い方

2017/12/15
WebAssembly(wat)を手書きする

2017/12/14
AngularJS を Angular に移行: ng-annotate 相当の機能を TypeScrpt ファイルに適用

2017/12/08
Android Thingsで4足ロボットを作る ~ Android ThingsとPCA9685でサーボ制御)

2017/12/06
Raspberry PIとDialogflow & Google Cloud Platformを利用した、3Dプリンターボット(仮)の開発 (概要編)

2017/11/20
カブクエンジニア開発合宿に行ってきました 2017秋

2017/10/19
Android Thingsを使って3Dプリント戦車を作ろう ① ハードウェア準備編

2017/10/13
第2回 魁!! GPUクラスタ on GKE ~PodからGPUを使う編~

2017/10/05
第1回 魁!! GPUクラスタ on GKE ~GPUクラスタ構築編~

2017/09/13
「Maker Faire Tokyo 2017」に出展しました。

2017/09/11
PyConJP2017に参加しました

2017/09/08
bravado-coreによるOpenAPIを利用したPythonアプリケーション開発

2017/08/23
OpenAPIのご紹介

2017/08/18
EuroPython2017で2名登壇しました。

2017/07/26
3DプリンターでLチカ

2017/07/03
Three.js r86で何が変わったのか

2017/06/21
3次元データへの深層学習の適用

2017/06/01
カブクエンジニア開発合宿に行ってきました 2017春

2017/05/08
Three.js r85で何が変わったのか

2017/04/10
GCPのGPUインスタンスでレンダリングを高速化

2017/02/07
Three.js r84で何が変わったのか

2017/01/27
Google App EngineのFlexible EnvironmentにTmpfsを導入する

2016/12/21
Three.js r83で何が変わったのか

2016/12/02
Three.jsでのクリッピング平面の利用

2016/11/08
Three.js r82で何が変わったのか

2016/12/17
SIGGRAPH 2016 レポート

2016/11/02
カブクエンジニア開発合宿に行ってきました 2016秋

2016/10/28
PyConJP2016 行きました

2016/10/17
EuroPython2016で登壇しました

2016/10/13
Angular 2.0.0ファイナルへのアップグレード

2016/10/04
Three.js r81で何が変わったのか

2016/09/14
カブクのエンジニアインターンシッププログラムについての詩

2016/09/05
カブクのエンジニアインターンとして3ヶ月でやった事 〜高橋知成の場合〜

2016/08/30
Three.js r80で何が変わったのか

2016/07/15
Three.js r79で何が変わったのか

2016/06/02
Vulkanを試してみた

2016/05/20
MakerGoの作り方

2016/05/08
TensorFlow on DockerでGPUを使えるようにする方法

2016/04/27
Blenderの3DデータをMinecraftに送りこむ

2016/04/20
Tensorflowを使ったDeep LearningにおけるGPU性能調査

→
←

関連職種

Recruit

バックエンドエンジニア(Python・Go)

業務内容

当ポジションは弊社Webサービスのバックエンド機能設計及び実装を担当します。 サービス毎の開発チームで2週間スプリントのスクラム開発を実施しています。 週次で開発チームミーティングを実施し、実装設計の相談や工数見積もりを行います。 全ての開発コードはレビューと自動テストによって品質を保っています。 また、リファクタリングやフレームワークのバージョンアップも開発フローに組込み、技術的負債を放置しない開発を目指しています。

フロントエンドエンジニア(TypeScript)

業務内容

当ポジションは弊社Webサービスのフロントエンド機能設計及び実装を担当します。 サービス毎の開発チームで2週間スプリントのスクラム開発を実施しています。 週次で開発チームミーティングを実施し、実装設計の相談や工数見積もりを行います。 全ての開発コードはレビューと自動テストによって品質を保っています。 また、リファクタリングやフレームワークのバージョンアップも開発フローに組込み、技術的負債を放置しない開発を目指しています。

インターン(Webエンジニア)

業務内容

業務から独立した、調査・研究系のタスクをおまかせしています。コードレビュー、 社内での報告会、 ブログ記事執筆を通して着実にスキルアップしていただくことを目指しています。 (希望があれば、プロダクトの開発業務もおまかせします。)

→
←

お客様のご要望に「Kabuku」はお応えいたします。
ぜひお気軽にご相談ください。

お電話でも受け付けております
03-6380-2750
営業時間:09:30~18:00
※土日祝は除く