みずぴー日記

月に行こうという目標があったから、アポロは月に行けた。飛行機を改良した結果、月に行けたわけではない。

🐙Githubブラウザ for iOS11

iOS11で追加されたFilesアプリから、Githubレポジトリを見るための拡張を作った。 AppStoreから入手できる。

OctoEye

OctoEye

  • HIROKI MIZUNO
  • Productivity
  • Free

Github->Octocat->Octopusという連想で、OctoEyeという名前にした。

⭐️使い方

OctoEyeをインストール・初期設定をすると、FilesのLocationsにGithubが追加される。

f:id:mzp:20170913210709p:plain

ここからレポジトリの内容をできる。

f:id:mzp:20170913211021p:plain

さらに、Textasticといった別アプリから開くこともできる。 ただし、保存はサポートしていない。

f:id:mzp:20170913211049p:plain

📦ソースコード

Githubブラウザなので、Githubに置いてある。

http://github.com/mzp/OctoEye

🎨デザイン

Sketch

最近Sketchを買ったので、事前にデザインを描いた。

f:id:mzp:20170913220733p:plain

今までは適当な紙に落書きするだけだったが、Sketchで描くと考慮漏れに気付けたり、単純に楽しかったりしてよかった。

アイコン

アイコンは最初いらすと屋のタコを使い、途中で自分で描いたのに差し替えた。

f:id:mzp:20170913212158p:plain

GithubなのでOctocatを使いたかったが、そのものを使うわけにはいかないので、一部だけ+シルエットにした。 猫耳だけを拡大したバージョンも作ったが今ひとつだったので段ボールバージョンを採用した。

f:id:mzp:20170913212447p:plain

ウォークスルー

初回起動時に出すウォークスルーを作った。 これは結構大変だった。

EAIntroViewの動きの確認。

iPadProで下書きをした。

f:id:mzp:20170913213039p:plain

雰囲気が分かったのでSketchでいろいろ描いて、デザインを検討した。

f:id:mzp:20170913213411p:plain

実装した。

f:id:mzp:20170913213450p:plain

🛠開発

Github Graphql

Githubから情報を取得するために、GraphqlAPIを利用している。 FileProviderExtensionではネットワークアクセスの回数を減らしたかったのでちょうどよかった。

ただバイナリデータを取得することができなかったので、そこだけはREST APIを使っている。

ReactiveCocoa/ReactiveSwift

レポジトリ追加画面などではReactiveSwiftを使っている。最初はRxSwiftを使おうとしたが、@にReactiveSwiftのほうが語彙がSwiftっぽいと言われたので、ReactiveSwiftにした。

イベントをmap等で加工するのは分かるが、画面上の要素とバインドする部分がよく分かんないまま書いていた。

スクリーンショット

AppStoreに提出用のスクリーンショットはfastlaneのsnapshotとframeitで生成した。

snapshotで各デバイス用のスクリーンショットを準備し、frameitでiPhone/iPadの枠をつけ上部に文字を入れた。

Xcode9からUITestが複数アプリに対応したので、Filesアプリの撮影も自動化できた。(がMetadata Rejectになったので、このスクショは削除した)

f:id:mzp:20170913220059p:plain

その他

  • CIはTravisCIを使った。 Xcodeの新しいベータが出て3〜4日でTravis側も更新されててすごかった。
  • ベータ版のOSにはスクリーンショットを外部に出せない制約が付くので、プルリクエストに画像を貼れなかった。

🚀今後の予定

  • Filesにあるタグ機能やお気に入り機能をサポートしてないのでやりたい。
  • masterブランチ以外は見れないので、切り替え機能をつけたい。
  • 書き込み機能もつけたいが、コミットメッセージをどうするかが難しいと思っている。

🍣入力メソッド

すべてのキー入力に対して🍣を入力する入力メソッドを作った。

コード

https://github.com/mzp/EmojiIM

入力メソッドの構成

入力メソッドによるテキスト入力は、各アプリケーションと入力メソッドが通信することで実現されている。アプリケーションはキー入力を入力メソッドに送信し、入力メソッドがアプリケーションに対してテキスト入力を行なう。

この通信のために入力メソッドはIMKServerクラスを用いてサーバーを起動する。そして各アプリケーションはクライアントとなり、サーバーと通信する。この通信を入力セッション(input session)と呼ぶ。

サーバーはクライアントごとに入力コントローラーを生成する。この入力コントローラーがキー入力からテキスト入力を生成する。

f:id:mzp:20170917183745p:plain

プロジェクトの作成

Xcodeでプロジェクトを作成する。Cocoa Appテンプレートを選ぶ。 Product nameはなんでもいいが、Bundle identifierにinputmethodという文字列を含める必要がある。

f:id:mzp:20170917183932p:plain

起動時の処理

入力メソッドが起動時に各アプリケーションと通信するための IMKServer を起動する。このとき、クライアントがサーバーに接続する際に使う名前を指定する。

import Cocoa
import InputMethodKit

private var server: IMKServer?

@NSApplicationMain
internal class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet private weak var window: NSWindow!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        server = IMKServer(name: "EmojiInputSession", bundleIdentifier: Bundle.main.bundleIdentifier)
    }
}

入力コントローラ

入力コントローラとして使うクラスを定義する。このクラスは IMKInputController の派生クラスにする必要がある。

このクラスの名前をInfo.plistで指定す。そのため、objc 属性を用いて、Swiftコンパイラによってマングリングされないようにする。

@objc(EmojiInputController)
open class EmojiInputController: IMKInputController {

キー入力時に inputText メソッドが呼ばれる。キー入力を処理したかどうかを真偽値で返す。

func inputText(_ string: String!, client sender: Any!) -> Bool

テキスト入力をしたいアプリケーションは client 引数として渡される。 ただし、 Any 型のままではテキスト入力を行なえないのでIMKTextInput プロトコルにキャストする。 キャストに失敗した場合は、入力メソッドが処理しなかったことを示すために偽を返す。

guard let client = sender as? IMKTextInput else {
  return false
}

IMKTextInputinsertText メソッドを用いて、入力したい文字列を挿入する。

client.insertText("🍣", replacementRange: NSRange(location: NSNotFound, length: NSNotFound))

Info.plist

Info.plistで入力セッションに使う名前や入力コントローラとして使うクラスの名前を指定する。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIconFile</key>
    <string></string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>$(PRODUCT_NAME)</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>LSBackgroundOnly</key>
    <string>YES</string>
    <key>LSHasLocalizedDisplayName</key>
    <false/>
    <key>LSMinimumSystemVersion</key>
    <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
    <key>NSHumanReadableCopyright</key>
    <string>Copyright © 2017 mzp. All rights reserved.</string>
    <key>NSMainNibFile</key>
    <string>MainMenu</string>
    <key>NSPrincipalClass</key>
    <string>NSApplication</string>
    <!-- 入力セッションに使う名前 -->
    <key>InputMethodConnectionName</key>
    <string>EmojiInputSession</string>
    <!-- 入力コントローラのクラス名 -->
    <key>InputMethodServerControllerClass</key>
    <string>EmojiInputController</string>
    <!-- 入力メソッドのアイコンとして使う画像 -->
    <key>tsInputMethodIconFileKey</key>
    <string>InputMethodIcon.tiff</string>
    <!-- 入力ソース(入力ソースの詳細はよくわからん) -->
    <key>TISIntendedLanguage</key>
    <string>ja</string>
   <!-- 入力ソースに対する識別子 -->
    <key>TISInputSourceID</key>
    <string>jp.mzp.inputmethod.EmojiIM</string>
    <!-- 入力メソッドが対応している言語のリスト -->
    <key>tsInputMethodCharacterRepertoireKey</key>
    <array>
        <string>Hira</string>
        <string>Kana</string>
        <string>Latn</string>
    </array>
</dict>
</plist>

インストール

ビルドして生成された EmojiIM.app~/Library/Input Methods にコピーする。Workspace settingsからDerivedDataをworkspace relativeにしておくと楽。 こんな感じでコピーする。

cp -r DerivedData/EmojiIM/Build/Products/Debug/EmojiIM.app ~/Library/Input\\ Methods

System PreferencesのInput Sourcesから追加できる。うまく反映されない場合は、いったんログアウトするとよい。

f:id:mzp:20170917185308p:plain

🙋つらい時に挙げる札

こういうのを作った。元ツイートが消えているので出典が示せないが、 死にたいにゃんシールが一番近いと思う。

f:id:mzp:20170715121106j:plain

🛒材料

Seriaでそれっぽいのを買った。

  • PPシート 不透明
  • 家具用のアルミパイプ

f:id:mzp:20170715112541j:plain

✂️制作風景

TextEditで縦書きテキストの画像を作った。フォントはヒラギノ明朝が一番それっぽかった。

f:id:mzp:20170827120227p:plain

この画像を調整して紙に印刷した。

f:id:mzp:20170715114718j:plain

PPシートに貼って、札の形に切った。両面テープがあると思ったらなかったので、糊で貼りつけた。

f:id:mzp:20170715120553j:plain

アルミパイプをガムテープで貼り付けて完成。

f:id:mzp:20170715121150j:plain

🚨利用例

作ったはいいけど使い道がなかったのでオフィスに持っていったら、肥大化したCSSを修正するときに使われていた。 「我々のオフィスにはカンバンはあるがアンドンはなかった。それがこれにより補完される」とかいう話をしてた気もする。

📝論文紹介: Software Development Waste(ソフトウェア開発におけるムダ)

ICSE 2017 勉強会のために論文を眺めてたら、リーンソフトウェア開発におけるムダについて調査した論文*1がおもしろそうだったので読んだ。

🐾背景: リーンソフトウェア開発

Womackはトヨタ生産方式を分析し、リーン思考を提案した*2。これは、「資源を消費し、価値を生まない活動」であるムダ(waste)の発見と除去を基本原則する。

リーンソフトウェア開発は、リーン思考とトヨタ生産方式をソフトウェア開発に適用した手法である。 リーンソフトウェア開発ではムダとして以下のものが挙げている。*3

トヨタ生産方式におけるムダ リーンソフトウェア開発におけるムダ
在庫 未完成の作業
加工しすぎ 再学習
作りすぎ 使われないコードや余分な機能
物の運搬 作業の引き継ぎ
手待ち 開発の遅れ
人の動作 作業の切り替え
不良・手直し 欠陥
価値 なし
活用されない才能 なし

しかし、ここでムダとされるものが妥当かを検証した研究はない。

🎯目的: ムダの発見

リーンソフトウェア開発には、どのような種類のムダが存在するかを調べる。

🔬調査方法: Pivotalにおける調査

Pivotalで行なわれたソフトウェア開発を対象とし、グラウンデッド・セオリーを構築した。

以下の3つの方法で、データを収集した。

  1. Pivotal Labのプロジェクトに参加し、観察した。 2年と5ヶ月間をかけて8個のプロジェクトに参加した。
  2. Pivotalの社員33人にインタビューを行なった。これにはソフトウェアエンジニア、インタラクションデザイナー、プロダクトマネージャが含まれる。
  3. レトロスペクティブ(ふりかえり)のトピックを分析した。 1年間の間に行なわれた91のミーティングを対象とした。

📊発見した9種類のムダ

調査の結果、9種類のムダを発見した。

誤った機能や製品の作成

誰も必要としない機能や製品を作ると、参加している全員の時間や労力が無駄になる。 これは、チームの士気やオーナーシップ、顧客満足度に影響する。

調査したプロジェクトにあった実例:

  • ペルソナにもとづいて開発をしたが、その後の調査でペルソナがあやまっていることがわかった。
  • マーケティングのために、ユーザの必要としない機能を追加した。

バックログ管理の失敗

プロダクトバックログの管理を失敗すると、プロジェクトの遅延や生産性の低下につながる。

調査したプロジェクトにあった実例:

  • 一度に複数の作業を進めることを優先したため、最初のリリースでは一部の作業が未完了のままだった。その機能は無効化されてリリースされた。
  • ビジネス側の要求に応じてユーザ登録プロセスを何度も変更したため遅延した。
  • プロダクトマネージャーが変更をくり返したため遅延した。

作業のやり直し

作業の遣り直しがムダなのは自明である。 これは以下の原因で発生する。

  • 技術負債。調査したプロジェクトの中には、リリース日を優先したため、リリース後に数週間にわたるやり直しが必要になったものがあった。
  • 作業中における欠陥の発。 調査したプロジェクトの中には、いくつかの画面を作ったのちに、モバイル対応が必要であることが発覚したものがあった。
  • ストーリーの拒否。実装が不十分のために、プロダクトマネージャーが受け入れを拒否することがある。
  • 完了条件が不明瞭なストーリー。調査したプロジェクトの中には、開発者が実装を完了したのち、ストーリーとモックアップにインタラクションが不足していることが判明したものがあった。

不必要に複雑な解決策

機能が不必要に複雑だと、ユーザの時間を浪費する。

調査したプロジェクトにあった実例:

  • 操作フローが、一部画面では左から右になっているのに、別の画面では上から下になっていた。
  • 複数のインタラクションデザイナーがいるため、レイアウト、リスト、警告、ボタンなどが複数作られた。
  • インタラクションデザイナーが2種類のフォームデザインを作ったために、複数のCSSが必要になった。
  • 顧客側の開発者が「複雑であればあるほど、重要な仕事をしていると実感できるのでよい」という態度でいることに開発者が不満をもらした。

本質でない認知負荷

認知負荷理論(Cognitive Load Theory)は、人間の作業記憶には限りがあるため、負荷が多すぎると学習や問題解決が阻害されるとしている。本質的な認知負荷とは、タスクをこなす上で避けられないものを指す。 一方で本質的でない認知負荷とは周囲の環境などに追加されるものを指す。*4

ソフトウェア開発には認知負荷が高いもの多いため、開発者の思考力は貴重な資源である。そのため、本質でない認知負荷はムダであるとみなす。 これは以下の原因がある。

  • 過度に複雑なストーリー。不必要に長く複雑で不明瞭なストーリーは開発者の作業記憶を消費してしまう。
  • 非効率なツール。 機能不足だったり複雑なライブラリや貧弱な開発環境、貧弱な開発プロセスなども含む。
  • 技術負債。 技術負債はコードの理解や変更を難しくする。 調査したプロジェクトの中には、テストスイートを実行すると大量の警告が出力され重要な情報が埋もれてしまうものがあった。
  • マルチタスク。複数のタスクを同時にやろうとすると、作業記憶に多数の情報を保持する必要があるため、認知負荷が増加する。

精神的苦痛

精神的苦痛は貴重なリソースである開発者を消費してしまう。 精神的苦痛は生産性の低下や燃え尽き症候群や欠勤、健康問題などにつながる。

調査したプロジェクトの中には、機能とリリース日が変更不能なプロジェクトがあった。 このプロジェクトでは、チームの士気や一体感が低下したり、メンバー間の問題解決に時間がかかった。

待機/マルチタスク

優先度の高い機能に着手できない場合、開発者は待ったり、優先度の低い機能に着手してしまう。 これはプロジェクトの遅延につながる。

調査したプロジェクトにあった実例:

  • 受け入れ環境が不安定だったため、プロダクトマネージャーが受け入れ作業を放置して別の作業を始めた。
  • ビデオ会議設備が不足しており待ちが発生した。
  • テストの実行に17分かかるため、開発者が別の作業をはじめてしまった。

知識の損失

知識の損失は、特定の知識をもったチーメメンバーがいなくなったときに発生する。 そして失なわれた知識を再獲得が必要になってしまう。

調査したプロジェクトの中には、全メンバーが入れ替わったプロジェクトがあった。 システムの理解に数ヶ月かかり、その間のベロシティはほぼ0になった。

非効率なコミュニケーション

非効率なコミュニケーションは不完全だったり誤解を産むコミュニケーションである。 チームの規模や非同期コミュニケーション、非対称なコミュニケーションが増えると、チームの生産性が低下する。

調査したプロジェクトにあった実例:

  • 移動に一時間かかるオフィスにチームが分割された。 リモートコミュニケーションを活用したが、次第にレトロスペクティブで議題にのぼるようになっていった。
  • ミーティングを特定の人が支配し、おとなしい人が意見を言えないようになった。
  • iOSチームが自身のプロセスにチームの決定をうまく反映できなかった。レトロスペクティブを繰替えすことで改善した。

🔁既存のリーンソフトウェア開発におけるムダとの比較

リーンソフトウェア開発のムダとされているものとは、以下の違いがあった。

  • 「引き継ぎのムダ」とされるものは観察できなかった。
  • 新規で 「不必要に複雑な解決策」「本質でない認知負荷」「精神的苦痛」「非効率なコミュニケーション」の4つを観察した。
  • それ以外は類似のものがあった。

まとめ

この論文ではソフトウェア開発におけるムダとその原因をエビデンスに基づいて示した。 そのために2年と5ヶ月をかけてデータを集め、グラウンデッド・セオリーを構築した。

その結果、既存のリーンソフトウェア開発におけるムダは支持されたが、いくつかの点で異なっていた。新規で4つ導入した一方、引き継ぎのムダは支持できなかった。

*1:Todd Sedano, Paul Ralph, and Cécile Péraire. 2017. Software development waste. In Proceedings of the 39th International Conference on Software Engineering (ICSE ‘17). IEEE Press, Piscataway, NJ, USA, 130-140. DOI: https://doi.org/10.1109/ICSE.2017.20

*2:J. P. Womack and D. T. Jones, Lean thinking: banish waste and create wealth in your corporation. Simon and Schuster, 1996.

*3:TABLE II: Comparison of Manufacturing Waste with Lean Software Development Wasteより引用

*4:http://miwalab.cog.human.nagoya-u.ac.jp/database/resume/2016-10-25.pdf を参考にした

🎤発表資料

勉強会の発表資料を作る際に気をつけていること。 最近だと📝The reason for using reason #ML_study - みずぴー日記を作った。

関係ない話は削る

発表で言いたいことがぼやけてしまうので、余分な話はしないようにしている。

自己紹介をいれると発表のテーマと違う話をすることになってしまうので、省略している。だいたいは「こんにちは、mzpです」くらいで済ませてる。

話の流れを整理するために事前に発表原稿を作っている。 何度か読み直して余分な話を削っている。 発表後は、このときの原稿をブログに貼り付けて、記事にしている。

f:id:mzp:20170727160306p:plain

導入は丁寧にする

「なぜこの技術が必要なのか」「なぜこの技術が登場したのか」といった動機の説明が不十分だと魅力がうまく伝わらないので、ここに時間を割く。

原稿や資料を作る際もここに時間をかけている。

「背景」→「困っていること/欠点」→「解決策」というストーリーにすることが多い。

後ろの席からも見やすいスライドにする

会場の形状によっては、前の席の人の頭でスライドの下のほうが隠れてしまうことがある。なので、文字を大きくしつつ、スライドの下のほうには空にするか補助情報だけを書くようにしている。

f:id:mzp:20170728071321p:plain

色を減らす

会場の明さやプロジェクターの性能によっては細かい色の区別がつけれないことがある。

なので、できるだけ使う色の種類は減らしている。地の文である黒と、強調したい部分を示す赤色系くらいにしている。

その他好み

凝ったテーマを使うことに気恥かしさを感じるので、KeynoteのWhiteテーマ(白い背景に黒い文字)を使っている。 タイトルの位置や文字のサイズなどは変更している。

明朝体が好きなのでスライドに使ってる。堅苦しい雰囲気がでてしまうが、みんなそこまで気にしないだろと思って、好みを優先させている。

📝The reason for using reason #ML_study

ML勉強会 #2で話した。

要約

f:id:mzp:20170722144752j:plain

近年のJavaScriptは進化が激しく、様々な拡張が提案されている。その中にはMLの機能を類似したものも多数ある。例えば、オブジェクトとレコード型のように使えるようにする拡張パターンマッチを導入する拡張といったものが提案されている。

しかし既存の言語にMLを意識した機能を追加するのは簡単ではない。

そこでFacebookのReasonそこでOCamlJavaScriptに近づける、というアプローチを採用してる。具体的には文法をJavaScriptに近づけたり、周辺ツールの整備を行なっている。

JavaScriptの必要性

Misoca

f:id:mzp:20170722144756j:plain

ボクはMisocaという請求書を管理するWebサービスを作る仕事をしている。今日の交通費も出してもらった。ありがとう!

React/Redux

f:id:mzp:20170722144805j:plain

Webサービスを作るには、JavaScriptを扱う必要がある。

JavaScirptフレームワークは数多く存在しているが、そのうち有力なものの一つにReactとReduxがある。 Misocaでも採用している。

React

f:id:mzp:20170722144808j:plain

Reactはview、要はHTMLを構築するライブラリである。 今回の話とはあまり関係がないので、詳細は省略する。

Redux

f:id:mzp:20170722144813j:plain

ReduxはJavaScriptプリケーションの状態を管理するためのライブラリである。 以下のような型を持つ関数によって状態を遷移を管理する。

state -> action -> state

state はその名のとおりアプリケーションの状態であり、action はなんらかの操作を表す。 そしてこの関数はreducerと呼ばれます。 名前が一般的すぎますね。

Reduxの話はちょっとしたいので、reducerの例をいくつか見ていきます。

例: counter reducer

f:id:mzp:20170722144817j:plain

数を数えるreducerは上記のように定義できる。 アクションの種類(type)が INCREMENTAL なら1を足す、 DECREMENT なら1を引く、そうでない場合はそのままの値を返すようになっている。

これでカウンターの値という状態を管理する。

例: todo reducer

f:id:mzp:20170722144824j:plain

もうちょっとアプリケーションっぽいreducerも紹介する。 TODOの完了状態を切り替えるreducerは上記のように定義できる。

各項目は、固有のIDと項目の名前、完了したかどうかなどと項目として持っている。 アクションは種類をあらわすtypeフィールドに加えて、どの項目の完了状態を切り替えるかどうかを示すidフィールドを持つ。

そしてこのreducerはアクションが TOGGLE のときは、idを確認する。 そして、idが一致したら完了状態のだけを反転させた新しい状態を作って返す。

reducer idiom

f:id:mzp:20170722144828j:plain

このtodo reducerはreducerでよく使われるイディオムが2つ登場している。

1つ目は TOGGLE で使った「typeがxのとき、yという項目を持つ」である。これは、OCamlのヴァリアント型と似ている。

次は完了状態の切り替えで使っていた「状態をコピーし、一部だけ更新する」である。これはOCamlのレコードの更新と似ている。

ML由来の機能

f:id:mzp:20170722144831j:plain

このように最近のJavaScriptにはML系から様々な機能が輸入されている/されようとしている。

他に導入されているもしくは導入が提案されている機能としては

  • パターンマッチ
  • Maybeモナドのようなnullに対する演算
  • 非同期モナドのようなコールバックの連鎖を回避する文法
  • 静的な型検査

などがある。

more ML features

f:id:mzp:20170722144835j:plain

これらの機能を実現するために各種ツールを利用する。 例えば拡張されたJavaScriptからブラウザで動くJavaScriptを生成するためのBabelだとか、JavaScriptに静的な型検査を導入するflowなどを使う。

OCaml

f:id:mzp:20170722144839j:plain

あーあ、こんなときにバリアント/レコード型があって、パターンマッチがあって、静的型検査があってJavaScriptが生成できる言語があればなー。あー。

あっOCamlでいいじゃん。あってよかった。じゃあ使いましょう。 便利。 めでたし、めでたし。

JavaScriptOCamlのギャップ

f:id:mzp:20170722144843j:plain

これで終われる世界はだいぶ幸せだが、そうもいかない。

JavaScriptOCamlの間には大きなギャップが存在している。 それは文法だったり、ツールの使い方だったりする。

これReasonの紹介スライドにあった画像だが、途方にくれてる感があって最高だと思う。

OCamlJavaScript

f:id:mzp:20170722144847j:plain

しかしJavaScriptにMLの機能を導入するよりも、文法やツールを追加してOCamlJavaScriptに近づけるほうが楽そうである。 少なくともReasonの開発チームはそう考えている。

Reasonがやっていること

ではReasonがJavaScriptプログラマに使いやすくするためにやっていることについて話していく。

BuckleScriptとの連携

f:id:mzp:20170722144859j:plain

OCamlJavaScriptを出力できるように、BuckleScriptというOCamlからJavaScriptを生成するコンパイラと連携している。

上記の通り、BuckleScriptは人間にもかなり読みやすいコードを生成する。

人間に読みやすいコード

f:id:mzp:20170722144904j:plain

「人間にもかなり読みやすいコード」というのをBuckleScriptはかなり重視している。 これはREADMEにのってる例ですが、自動生成したとは思えないコードになっている。

中間言語であるlambdaからJavaScriptを生成しているこれができる、バイトコードから変換しているjs_of_ocamlとの重要な違いだ、とマニュアルに書いてあった。

余談: OCamlの魅力

f:id:mzp:20170722144908j:plain

BuckleScriptの資料が「なぜJavaScriptを使うか」という疑問に対しては、ブラウザで動く言語で…とかいろんな場所で動いて…とかいろいろと説明している一方、「なぜOCamlなのか」という疑問に対しては「もうしってるでしょ」ですませていた。格好いいと思う。

文法: 変数束縛

f:id:mzp:20170722144912j:plain

次は文法についてです。 JavaScriptの雰囲気にあうように文法が変更されている。

例えば変数束縛はletのあとのinをつけなっているし、行末には ; をつけるようになっている。

文法: 条件分岐

f:id:mzp:20170722144916j:plain

条件分岐も中括弧で範囲を示すようになっている。

文法: パターンマッチ

f:id:mzp:20170722144919j:plain

パターンマッチはmatchからswitchにキーワードが変更になっている。中括弧を明示してるのでネストしてもややこしくならない!ってマニュアルに書いてあった。

パッケージマネージャ

f:id:mzp:20170722144925j:plain

パッケージマネージャーはJavaScriptのnpmをそのまま使っている。 Reason自体も各種ライブラリのバインディングもnpm経由でインストールできる。普段と同じパッケージマネージャが使えるので、JavaScript使いには親切になっている。

が、npm install bs-platformするとOCamlのダウンロードとビルドがはじまるのはアツいと思う。

コード補完: Merlin

f:id:mzp:20170722144929j:plain

いくつかの周辺ツールも用意されている。 OCamlの補完ツールであるmerlinはそのまま使える。

エディタ拡張

f:id:mzp:20170722144932j:plain

各種エディタの拡張も用意されている。 Reasonはコードフォーマッタもあるので、それもエディタから使えるようになっている。

BeterErrors

f:id:mzp:20170722144937j:plain

既存のOCaml toolchainのラッパーもある。

例えばBetterErrorsコンパイラのエラーを整形し、 式のどの部分で型を間違えているかをわかりやすく表示する。 エラーメッセージの整形をしているだけなので、既存のコンパイラとパイプとつないで使うことができる。

RED

f:id:mzp:20170722144942j:plain

またocamldebugの使い勝手を改善するREDというツールもある。

余談: Reasonという名前

f:id:mzp:20170722144946j:plain

気付いてると思うが、Reasonという名前は検索しづらい。というかFacebookのだしてるライブラリはflowだとかinferだとか検索しにくい名前ばかりである。

Reasonという名前のせいで、exampleの名前がおもしろくなっている。 ライフゲームが実装されたイグザンプルのプロジェクト名がreason-of-lifeなのは最高だと思う。

Reasonの利用例

Reasonがどのあたりで使われているかの話をします。

Facebook Messanger

f:id:mzp:20170722144954j:plain

具体的にどの部分かは分かりませんがFacebookメッセンジャーの25%はReasonに書き換えられているらしい。このTweetには書いてないですが、ブラウザ版の話らしい。

React

f:id:mzp:20170722144957j:plain

Reactのプロトタイピングにも使われている。 JSXをサポートしてたりと、やたらReactのサポートが厚いのが気になっていたんですが、このためかもしれない。

Trello.md

f:id:mzp:20170722145001j:plain

ボクもReasonつかって、Chrome拡張を書いたChrome拡張のバンディングは存在してなかったので、必要な部分だけ自分で書いた。

Reasonのよい面

f:id:mzp:20170722145005j:plain

パターンマッチ最高

f:id:mzp:20170722145009j:plain

予想通りですがパターンマッチ+レコード更新の組み合わせは最高だった。 「各アクションのパラメータが型で保証される」「返り値の構造が変わっていないことが型で保証される」「すべての分岐を網羅していることをコンパイラが保証してくれる」というあたりがよい。

ビルド速度

f:id:mzp:20170722145013j:plain

BuckleScriptはOCamlのLambdaIRを変換した上で、末尾呼び出しの除去や定数畳み込み、インライン化などの最適化をなった上で、JavaScriptを生成する。 一見時間がかかりそうに見えるが、JavaScriptにしたあとの処理のほうが遅いので、あまり気にならなかった。

f:id:mzp:20170722145017j:plain

JavaScriptにはnullやundefinedみたいな「無」みたいな値がある。 これをfunctional扱えるようなモジュールが用意されている。

sがnullでない場合のみ内容を出力する関数、sがundefinedの場合のみ内容を出力する関数は上記のようになる。

callback hell

f:id:mzp:20170722145021j:plain

JavaScriptでは通信はコールバックで書く必要がある。それはいわゆるcallback hellを招く。このコードはPHPですが。

OCamlには非同期モナド(lwt)があるので、これを利用して >>= によるチェーンで書ける。 これは DelayedInc が発行されるのをまち、その後1000ms秒待ち、 Inc を送信しています。

Reasonのつらい面

次はつらい側面について話す。

インストール時間の増加

f:id:mzp:20170722145032j:plain

ReasonはOCamlコンパイラをforkして作っている。 そのため、インストール時にOCamlのビルドが必要になる。

そのため別マシンに移ったときなどにOCamlのビルドが毎回走ってしまう。CI上でOCamlのビルドが毎回はしるのでつらい。

大量の型定義

f:id:mzp:20170722145036j:plain

JavaScriptライブラリを使う箇所では、大量の型定義が必要になる。こんな感じの定義が延々と続くことになる。つらい。

JSON

f:id:mzp:20170722145040j:plain

JSONから情報を取得するのも大変で、一段ネストをすすめるためにパターンマッチが必要になる。

バリアントのランタイム表現

f:id:mzp:20170722145043j:plain

先ほど話した通りパターンマッチとバリアントの組み合わせは便利。

しかし、ランタイムではバリアントの名前が失なわれるため、デバッグがつらい。多相バリアントにするともっと難しくなる。

よくわかってない部分

Reasonに関してよくわかってない箇所の話をしていく。

vimハイライト

f:id:mzp:20170722145050j:plain

vimプラグインも準備されいるが、 キーワードのハイライトがおかしい。 たとえば when はキーワードだがハイライトされない。一方で普通の識別子である box はハイライトされる。

プラグインソースコードを読むとforked from rustと書いてあり、ハイライトがおかしい原因は分かる。 が、なんでそのままになっているか分からない。

JSCaml

f:id:mzp:20170722145055j:plain

JSCamlはFacebookが出しているJavaScriptOCamlに変換するコンパイラである。 念のためにもう一度いいますが、JavaScriptOCamlに変換するコンパイラである。

READMEに「ReasonでJavaScriptライブラリを使いたいときに、いったんJavaScriptにすると便利」とか「貧弱なデバイスJavaScriptを実行したいときに便利」と書いてある。なんなんだ。

esy

f:id:mzp:20170722145059j:plain

ReasonはJavaScriptのパッケージマネージャーnpmをそのまま利用している。 しかし、Reason/OCamlのようなビルドが必要な言語との相性はあまりよくない。

そこで、yarnをforkし、ソースコードの取得とビルドを明確に区別できるようにしたesyが開発されている。

opamブリッジ

f:id:mzp:20170722145103j:plain

またopamとのブリッジも用意されているので、npmのライブラリもopamのライブラリもインストールできる。そのため、npmのレポジトリに大量のopamのライブラリが登録されている。

迫力がある。

参考文献

f:id:mzp:20170722145107j:plain

この資料を作成するにあたって参考にしたものをあげておきます。

  • awesome-reason。reasonの各種資料へのリンク集。ツールや発表資料へのリンクがある。
  • Dawn of Reason。 途中で何度か図を引用してます。OCamlJavaScriptに近づければいいんだ!!という話が書いてあった。
  • Reason guide。reasonの文法などはここに書いてあったり書いてなかったりする。書いてないやつはOCamlからの連想でなんとかなる。
  • Bucklescript manual。 BuckleScript部分、つまりJavaScriptとの連携部分のマニュアル。が、文法はOCamlなのでReasonで使えたり使えなかったりする。

まとめ

f:id:mzp:20170722145112j:plain

  • JavaScriptにはMLの機能が必要で、どんどん導入されている。
  • でも大変なので、OCamlを使ったほうがいい。
  • そのままだと大変なので周辺環境を整備したのがReason

以上です。

✈️北海道ワーク

先週一週間は北海道の旭川で過した。 長期休暇を取ったわけではなく、昼間はリモートで勤務をした上で夕方から観光していた。

f:id:mzp:20170617083720j:plain

🏠宿

有給消化中の友人と一緒にAirbnbで部屋を借りた。 1週間でたいだい6万円くらい。

調理器具がついてたので、近所のスーパーで買ってきた魚やジンギスカンを焼いて食べていた。 焼くだけでだいたいうまい。

f:id:mzp:20170620084544p:plain

f:id:mzp:20170621001225p:plain

f:id:mzp:20170621001302p:plain

🏢リモートワーク

部屋についているWifiで仕事をしていた。通信速度などが不安だったが、問題なかった。念のため、市内のコワーキングスペースの場所を確認しておいたが、必要なかった。

普段通り仕事をしていたので、北海道にいることは気づかれなかった。 途中でTwitter経由でバレた。

👣市内観光

早めに仕事を開始して、17時くらいには仕事を終わらせ、その後、市内の観光をしていた。

櫻子さんの足下には死体が埋まっている」は旭川が舞台なので、市内の登場した場所にいくつか行った。びーとるのたびにっき 『櫻子さんの足下には死体が埋まっている』(TVアニメ版その①)~北海道・旭川エリア(旭川市・当麻町・美瑛町)~【舞台探訪(聖地巡礼)】がバスでの行き方も記載してあり、便利だった。

手。

街中に急に手がでてくるので、びっくりする。🙌。

f:id:mzp:20170612180455j:plain

ロータリー

f:id:mzp:20170611152624j:plain

ティーハウス ライフ・ラプサン

サイトによって日本最北の紅茶専門店と書いてあったり書いてなかったりする。 よく分からない。

f:id:mzp:20170613181556j:plain

シフォンケーキが登場していたので食べた。

f:id:mzp:20170613183558j:plain

ダンデリオン

ケーキ屋。ちょっと離れた場所にあるので、バスでは行けなかった。 レンタカーを借りた日に行った。

f:id:mzp:20170617173653j:plain

かぼちゃのモンブランが登場していたが、時期が違うらしく売っていなかった。モンブランは売り切れていた。

f:id:mzp:20170617211018j:plain

🍽食事

何を食べてもおいしかった。

うに

駅前のイオンで塩水うにが1500円くらいで売っていたので買った。 めっちゃうまい。

f:id:mzp:20170620233246p:plain

f:id:mzp:20170620233329p:plain

寿司

寿司は文句なしにうまい。

f:id:mzp:20170621090016p:plain

f:id:mzp:20170621085933p:plain

旭川ラーメン

旭川ラーメンも何店舗か食べにいった。

ぞい。

f:id:mzp:20170612195534j:plain

みそバターラーメンには、バターが予想の倍くらい入ってる。

f:id:mzp:20170613193028j:plain

地ビール

地ビールが何種類があるらしいので、飲みにいった。翌日寝坊すると飛行機に乗れなくなるという状況で飲むビールは、スリリングだった。

f:id:mzp:20170616180528j:plain

4種飲み比べセットを頼んだら、なぜか5種類でてきた。

f:id:mzp:20170617190635j:plain

🚗観光

週末+有給で周囲を観光した。

旭山動物園

一度行ったことがあるので、そこまで感動しないかなーと思ってたけど、すごかった。迫力がすごい。

f:id:mzp:20170614141805j:plain

f:id:mzp:20170620234552p:plain

f:id:mzp:20170620234647p:plain

f:id:mzp:20170614102301j:plain

青い池

macOSの壁紙になっている青い池を見にいった。 時期と時間が違うので、壁紙とはだいぶ印象が違うけどキレいだった。 次は冬に行きたい。

f:id:mzp:20170617094755j:plain

向う道はひたすらまっすぐだった。

f:id:mzp:20170620235207p:plain

富良野

青い池のあとは富良野に行った。 広大な土地に花が整然と植えてあるのは独特の迫力がある。

f:id:mzp:20170617114743j:plain

f:id:mzp:20170617154800j:plain

富良野オムカレー推してるので食べた。うまい。

f:id:mzp:20170617125930j:plain

✨感想

知らない街を散歩したり、地元の名物を食べるのが好きなので、働きながらしばらく滞在できるのはよかった。Airbnbブログで掲げられている「暮すように旅をしよう」はだいぶ好みにあっている。

ホテルに泊まるのに比べて自炊の割合が増えるのが心配だったか、スーパーで買ってきた何かを焼けばおいしくなるので問題はなかった。北海道だからな気はしている。

とはいえ、自宅に比べるとモニタの数が不足していたり、ネットワーク環境もそれほどよくなかったりと、めっちゃ作業が捗るという感じではなかった。

総合的によい体験だったのでまた行きたい。