30億のデバイスで走るHonMarkHunt

JavaとJavaScriptと恐竜の絶滅について書いていきます。

2018年振り返り

前置き的な

12月。

めっきり寒くなってきた。

肌寒い季節は過ぎ、気づけば最低気温は一桁の日々が続く。皆街を歩く体は丸まり、小さくなった体から精一杯の白い息と愚痴を吐き出す。

クリスマスと連休が近づき、浮き足立つ人々とそれを皮肉る人々。これも毎年のことだ。いつもと違うのは

「平成最後の」

テレビやラジオはこの言葉ばかりだ。

結局は皆いつもと違う何かを見つけたいのだ。毎年毎年同じ日々を繰り返している。そうではない。今年はいつもと違ったのだ。意味のある年を過ごしたのだ。そう思いたいだけなのだ。

俺は今平成最後のポエムを書いている。

なぜ年の終わりにポエムを書いているのか。

そう。Advent Calendarだ。

Advent Calendarとはなにか?

クリスマスまでの日々を1日1日楽しむためのものだ。

どこでどう間違えたのか、エンジニアはクリスマスまで毎日記事を書く。そもそもクリスマスなど28歳にもなれば楽しみではない。別になんら楽しみではない日に向かってお互いに同調圧力をかけながら疲弊しながら記事を書いている。この行為に悲しみ以外のなんの意味があるのだろうか。

突然だが、私は今一つ嘘をついた。

そう。私は28歳ではない。29歳なのだ。

さっき29歳になった。

Happy Birthday to 俺。

おめでとう 俺。

そう、俺は今誕生日当日に Shinjuku.LT アドベントカレンダー を書いている。時間は間も無く丑三つ時を迎えようとしている。嫁は先に寝た。

一体なにをしているんだ 俺。

目を閉じ少し想いを馳せる。

あれは12月に入る少し前だっただろうか「Advent Calenderは気楽に書こう!エモいことでもいいよ!」そう話した日々が懐かしくすら感じる。

目を開け現実を受け入れる。

f:id:HonMarkHunt:20181218022852p:plain

気楽とはなんなのか?

Property-Based-Testing ってなんだ?聞いたこともないぞ?

gRPC なんて全然わからないのにもうMock化の戦略立ててるのか?

俺はそっと「CodePipelineなんか大変だゾォ」という記事をそっと閉じる。タイトルからしてバカの福袋大安売りバーゲンだ。

繰り返す。

俺は今平成最後のポエムを書いている。

ポエムしか書くことがないからだ。

Only ポエム。

ポエム Only。

俺はポエムマン。

さぁ。本編のMAKUAKEだ。

2018年振り返り

毎年Advent Calendarの時期になると今年何やったかを振り返る。大体全く思い出せない。

去年のこととなるともはや不可能だ。全く何も思い出せん。

大した人生を送っていないからなのは明白だが、なんとなく寂しいので1年振り返り的なことを書いて来年以降も振り返れるようにしておく。

読む人の気持ちは全く考えていない。許してほしい。ポエムマンにはこれしかできない。

結婚した

6月に入籍した。

f:id:HonMarkHunt:20181218025102p:plain

「おいおいー!いきなり6月かぁい!」と思ったかもしれない。しかし入籍までの道は以外遠い。両家に挨拶に行き、両親同士の顔合わせを済ませ、必要書類を揃え、日取りを決めなければならない。

さて、多くの場合結婚は人生において1,2を争うビッグイベントだ。しかし、結婚して何か変わったかと言われれば意外と何も変わらなかった。しばらく一緒に暮らしているので役所のDBにCRUDが一回走ったぐらいだろう。

夫の立場でいうことではないかもしれないが、エンジニアと結婚したからといって特に他の職種の人との差はないと思っている。

clown.hatenablog.jp

こんなエントリがある。

何一つ一致するところがない。

割と普通の日々だ。

ただ今までより毎日がちょっと楽しい。

俺ぐらいの徳の低い人生にはそれくらいでちょうといいのかもしれない。

結婚式

9月に結婚式を挙げた。

f:id:HonMarkHunt:20181218031119p:plain

「おいおい〜!また結婚ネタかぁい!」と思ったかもしれない。はっきり言おう。結婚式は想像の10倍過酷だ。大体丸3ヶ月は準備に費やす。長くなるのでここでは詳しく話さないが本当に過酷だ。おそらく夫婦で挑むイベントとしては最大なのではないだろうか。

その分思い出に残ったし、当日は本当に楽しかった。

誤解を恐れずいうと、男性目線だと結婚式なんてやりたくないと思う人が多いかもしれない。俺もそうだった。

ただ当日が終わってみれば「もう一回やりたいなぁ〜」と思った。

ご参考まで。

エンジニアリング的な話をすると式までのタスクをtrelloで管理したりした。友人は招待状をGoogle Formで作っていて、こいつは天才だなと思ったりした。

意外と結婚式はハックできる。

ただハックすることが目的ではないのでそれだけは忘れない方が良さそうだ。

ちなみに結婚式の景品にポエムを渡した。

f:id:HonMarkHunt:20181218032543j:plain

お久しぶりです。ポエムマンです。

転職

10月に転職した。

f:id:HonMarkHunt:20181218032652p:plain

株式会社ビズリーチに入社しました。

厳密に言えばともともとはフリーランスだったので転職というのか微妙なところではあるが、正社員としてjoinしたので転職といえば転職かもしれない。

転職の理由は色々あるが愚痴っぽくなるので割愛。ただ結構ポジティブな理由で動けたのでよかった。

  1. 給与
  2. 作るもの
  3. 働き方
  4. 技術力

この辺を大事にしながら活動した。ちょっとだけ掘り下げておく。

給与

言わずもがな。一人ならまだしも家族がいると「やりたいことがあるから給与減らしてでもやりたい!」という判断は厳しい。そもそもそこまでやりたいこともなかったのでお給金は高ければ高いほどよかった。

作るもの

せっかく正社員になったのだから人のためになるものを作りたい。という気持ちがあった。

以前はインターネット広告関連の仕事をしていたのだが。「これ誰が幸せになるんだ?」と考えて考えていなかったりした。

なんとなくのイメージだが、もし子供ができて大きくなった時に「パパスはこんなサービスを作ってるんだぞぉ!」と、胸を張って言える仕事をしたかった。「パパはインターネットに広告を出してるんだゾォ」といった時に「これ間違えてクリックするからうぜぇんだよ!」と、言われる気がした。

働き方

はるか昔は本番障害で丸三日徹夜で働いたりしてた。

まあエンジニアならよくある話かもしれない。

業務が 忙しくて/楽しくて 家でも仕事をする。

まあエンジニアならよくある話かもしれない。

ただ、せっかくこんな俺が結婚できたのだ。家では待っててくれる奥さんと過ごしたいなあ。と思ってる。なんとなくそのために働いてるし、そのために生きてるような気がする。奥さんと一緒にいる時間を削ってまで働くことはしないと決めた。

簡単にいうと働き方が自由で残業も多くないとこがよかったっていう話。

技術力

上記を満たしていればある種二の次ではあったが。今更Struts1で開発だあ!と言われてもそれは厳しい。今後のことも考えればできれば新し目の技術を触りたいし提案していきたいなぁとか考えていた。

2018年総括

ざっと振り返ると大きなイベントごとの多い年だったなと思う。

エンジニアリング的なところでいうと、なんとなく4,5年やってきたことが(遅すぎるが)繋がってきた気がする。

あの日悩んだことやみんなで話したことや後悔したこととか成功したこと。そういうのが今すごく価値になっている感じしている。まだまだ足りないことは多いが、何が足りないのか足りないことを補うためにはどう学べばいいのか学んだことをどうすればいいのか。それがわかってきた感じがして、今は仕事が楽しい。

Shinjuku.LTもちょっとづつ盛り上がりながらずっと続いている。 なかなか言うタイミングがないのでここで言っておくが、2年前にShinjuku.LTが発足してからしばらくは、自分が未熟で周りに迷惑や不快な思いを与えていたと今すごく思います。本当にすみませんでした。 自己顕示欲の塊みたいな俺に怒りもせず付き合ってくれて2年間以上Shinjuku.LTを一緒に続けてる仲間に改めて感謝。これからもよろしく。

反省点

去年末から 引っ越し -> 結婚 -> 結婚式 -> 転職 と続いていたのでかなりばたついた一年間だった。今人的に一番反省したのはこれ。

f:id:HonMarkHunt:20181218035825p:plain

connpassの勉強会の申し込みが [2017/10 ~ 2018/09] 一年間空いていた。 (connpass以外で行ってたりしたが視覚的にびっくりした)

少し落ち着いたので勉強会参加増やしていこうと思う。

別に勉強会に行けば偉いと言うわけではないが人との繋がりはShinjuku.LTですごく学んだので繋がりを増やす意味でも参加していきたい!

2019年

最後にちょっとだけ来年について。

2019年は積極的に登壇回数を増やしいこうと思う。

やっぱりいいアウトプットになるし会社の宣伝にもなるし繋がりも増える気がしている。

そんな感じ。

now.shとCircle CIで継続的音速サービスリリース

f:id:HonMarkHunt:20181201173532p:plain:w200

この記事は 2018年 Shinjuku.LTアドベントカレンダー の1日目の記事になります!

2日目の記事はこちら =>

vue-cliとNetlifyで始めるお手軽サイトホスティング - Qiita

Shinjuku.LTってなに?って方はこちら => https://shinjukult.netlify.com

はじめに

Shinjuku.LTはコミュニティーのサイトをOSSとして開発しています!

github.com

簡単な構成

f:id:HonMarkHunt:20181201181102p:plain

本日はバックエンドのリリースフローを簡単にご紹介します!

https://github.com/shinjuku-lt/shinjuku-lt-backend

  1. now.shでデプロイ
  2. Circle CIで自動化

1つづつ見ていきましょう!

now.shでデプロイ

zeit.co

now.shって何?

now.shはzeit(ツァイト?)社が作成しているPaaSです。一言で説明すると 「コマンド一つでデプロイできるサービス」 できます。超早いし、超楽です。最高。あと無料です。無敵。

これは個人的な感想ですが、趣味や勉強で開発しているプロダクトであれば現状最高のプラットフォームだと思います。

  • node.js ()
  • static page
  • docker

でできたプロダクトであれば利用できます。

簡単にやってみましょう!

# install now.sh
$ npm install now -g

# 適当にexpressのアプリを作成
$ mkdir myapp
$ cd myapp
$ npm init
$ npm install express --save

index.jsを作成し以下の内容を記載

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => res.send('Hello World!! from now.sh!!'))

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

念の為起動確認

$ node index.js

$ curl http://localhost:3000
> Hello World!! from now!!

アプリができたので早速デプロイしてきましょう!

$ now
> Synced 3 files (13.25KB) [1s]
> https://myapp-xxxxxxxxxx.now.sh [v2] [in clipboard] [1s]
> ┌ **        Ready               [499ms]
> ├── package-lock.json
> ├── index.js
> └── package.json
> Success! Deployment ready [8s

# 念の為動作確認
$ curl https://myapp-xxxxxxxxxx.now.sh
> Hello World!! from now!!

デプロイが終わりました!!(nowコマンドは初回のみ) こちらがデプロイ後のURLです! => https://myapp-xxxxxxxxxx.now.sh

Circle CIで自動化

now.shを使用してデプロイが完了しました しかし色々問題が残っているので解消しましょう!

nowコマンドでデプロイした後の改善点

  1. チームで安定的なデプロイができない
  2. ドメインが固定されない
  3. デプロイがローカル依存になる

この辺解決しながらCircle CIで自動化していきます。

チームで安定的なデプロイができない

nowコマンドはlocalの~/.now/auth.json中身を見て実行されます。複数人で同一プロダクトを開発する場合かなり厄介です。 また、CIからnowコマンドを実行しようとした場合認証情報がないのでエラーになり、メールでの認証も行えないのでコマンドが実行できません。

$ now -t ${YOUR_TOKEN}

ここからtokenを払い出してloginしていなくても、CI上からでもnowコマンドが実行できます!

ドメインが固定されない

nowコマンドでデプロイすると基本的にはhttps://myapp-xxxxxxxxxx.now.shランダムなURLが毎回発行されます。サービスのURLがデプロイのたびに変わっていたら由々しき事態なので毎回固定しましょう。

$ now alias ${ALIAS_YOU_WANT}

毎回コマンド打つと大変なのでnow.jsonで固定できます。

https://github.com/shinjuku-lt/shinjuku-lt-backend/blob/master/now.json

デプロイがローカル依存になる

チーム開発などの場合にはデプロイのたびにnowのインストールとtokenの共有が必要になります。 GitHubでPRレビューなどした場合は、レビューが終わってマージ後に手動でデプロイ。などのめんどっちいオペレーションが発生します。 せっかくの音速デプロイなので完全自動化しましょう!

GitHubのmasterの変更をtriggerにCircle CI(CIは別になんでもいいと思います)を設定して、環境変数に先ほどのtokenを登録して置くだけでデプロイの自動化ができます!

サンプルで、Shinjuku.LTのバックエンドで利用しているCircle CIの設定を貼っておきますのでご参考になさってください!

https://github.com/shinjuku-lt/shinjuku-lt-backend/blob/master/.circleci/config.yml

version: 2
jobs:
  build:
    docker:
      - image: circleci/node:8.9

    branches:
        only:
          - master

    working_directory: ~/repo

    steps:
      - checkout

      - run:
          name: install now
          command: sudo npm i -g --unsafe-perm now

      - run:
          name: deploy asap now!!
          command: |
            now shinjuku-lt/shinjuku-lt-backend --public -t ${ZEIT_TOKEN}
            now alias -t ${ZEIT_TOKEN}

最後に

now.shとCircle CIで無料で高速デプロイする方法を紹介しました!ちなみにnow.shは最新バージョンの2.0がリリースされており。現在移行作業中です。

now.sh 2.0 を利用中の場合は本記事の内容が正しく動作しない場合があるかもしれませんのでご注意ください。

第一回 scala.rookies 行ってきたメモ

https://connpass-tokyo.s3.amazonaws.com/thumbs/44/51/4451eef882af829ea79ef2bbd3a62ae9.png

こちら行って来た

scala-rookies.connpass.com

「初心者向けscalaの勉強会/登壇場所があってもいいんじゃない?」「無いなら作ろう」ってことで開催したらしい。最高。

アジェンダ

  1. 運用を続けていくためのScalaの書き方(仮)
  2. Query API 試行の旅 (仮)
  3. 未経験からscala始めるときの天国と地獄(仮)
  4. OrderingとOrdered
  5. 未経験でScala案件投げ込まれて無事生還した話
  6. 怖くない!implicit!
  7. 実践Scala入門が読みたくなるようにプレゼンさせてください

運用を続けていくためのScalaの書き方(仮)

登壇者

twitter.com

  • ホテルのロビーからリモートで配信
  • 実際scalaで運用してみて、メンテナンス向上、バグ削減しやすい書き方

モナドトランスフォーマーは外部に出さない

DBとかのトランザクションは積極的に例外吐く

  • 音声途切れてよく聞こえなかった
    • 悲C
  • 例外でロールバックされるから素直に例外投げたほうが見通しがいいって話っぽい
  • この辺はある程度ORMのライブラリに依存する話?

副作用は無理に型で表現しない

  • ちょっとわからなかった

DBやクライアントの返却地の型をそのまま使い回さない

  • 自動生成便利だけどアプリレイヤーで使用するものは自前で作ろう!って話
  • 変更に強くなるからっぽい

感想

  • リモート音声と視力の悪さと頭の悪さであんまりわからんかった。あとで資料見たい。

Query API 試行の旅 (仮)

登壇者

twitter.com

  • Datastore(DB, API)の境界線をどうやってscalaで表現するかって話
  • レイヤードアーキテクチャ
    • 低レイヤの方の制約を強くする
    • 今日はRepositoryの話
  • 結構今のプロダクトの構成に似てる気がする
  • Resource(= Repository) + Queryで柔軟に表現できるっぽい
  • 実装されたQuery、どれが呼ばれるかってどうやって判定するんだろう
  • 初耳 https://monix.io/
  • 非同期処理いい感じらしい

未経験からscala始めるときの天国と地獄(仮)

登壇者

twitter.com

  • cdコマンドも知らなかったけど1年間scala学んで困ったこと
    • すごい頑張ってる感じがしてすごい
  • 成長環境が用意されている感じと伸び伸び学んでる感じがしていいなーと思った
  • よくわからないって人前で言えるのは中々いいことな気がする
  • 一緒に頑張ろうって感じがした

OrderingとOrdered

登壇者

twitter.com

資料

  • case classをソートしたい
  • Orderingをimplicitにできる
  • 勢いあって面白かった

未経験でScala案件投げ込まれて無事生還した話

登壇者

twitter.com

  • scalaだけが仕事の全てでは無い
    • たしかに
  • scala.yokohama行ってみたい

怖くない!implicit!

登壇者

twitter.com

資料

www.slideshare.net

実践Scala入門が読みたくなるようにプレゼンさせてください

登壇者

twitter.com

  • Scala実践入門買おう

Scala implicit デザインパターン

Scala implicit デザインパターン

implicit。書いてあるコードは読めるけど自分で実装する時に使いどころがワカン。」 みたいのがあって職場の人に聞いたらいい感じのリンクを教えて頂いたので翻訳しつつ勉強がてらメモ。

目次

  1. 最初に
  2. Implicit Contexts
  3. Type-class Implicits
  4. Derived Implicits
  5. Type-driving Implicits
  6. まとめ

最初に

しばしば貧弱Scalaエンジニア(俺)達から畏敬の念とともに語られるimplicit。実はそれ自体の機能はそんなに強力じゃないみたい。

  • implicit parameter : 明示的に引数のを渡す必要なく、その型とスコープ内の値に基づいて自動的に推論
  • implicit conversion function : 要求に応じて明示的に関数を呼び出す。

ただ単純に使用するのではなく、長い歴史と数多くのエンジニアたちの知恵を礎に最適な使用方法(=デザインパターン)が生まれた。4つほど紹介してみる。

Implicit Contexts

DB Connectionとか 何回も何回も使用する引数を、渡す度に定義してたらめんどくさいよね。値があるなら空気読んでそれ使ってよ。 という話。

ex) 非同期処理する全てのメソッドにExecutionContextを引き渡す

// 全てのメソッドが`ExecutionContext`を必要としている
def getEmployee(id: Int)(implicit e: ExecutionContext): Future[Employee] = ???
def getRole(employee :Employee)(implicit e: ExecutionContext): Future[Role] = ???

getEmployee(), getRole()の両方でカリー化された引数でExecutionContextを要求している。

// implicitではない雑魚
val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global

val bigEmployee: Future[EmployeeWithRole] =
  getEmployee(100)(ec).flatMap { e =>
    getRole(e)(ec).map { r =>
      EmployeeWithRole(e.id, e.name,r) 
    }(ec)
  }(ec)

implicitを使わない場合全ての呼び出し箇所で明示的に(ec)を引き渡す必要がある。すごくめんどくさい。 4つならまだいいけどもっと数が増えてきたり、複数箇所で使用されていたらもうめんどくさすぎる。

// implicit parameterを使うぞ!
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global

val bigEmployee: Future[EmployeeWithRole] =
  getEmployee(100).flatMap { e =>
    getRole(e).map { r =>
      EmployeeWithRole(e.id, e.name,r) 
    }
  }

implicitキーワードとともにExecutionContextを定義することで、全ての使用箇所で明示的な引数の引き渡し(ec)を全て削除できる。やったねたえちゃん!という話。

Implicit Contextsの定義

  1. 通常ジェネリックス型ではなく、型パラメーターも持たない。
  2. 異なるシグネチャを持つあらゆる関数に同じimplicitが渡される。
  3. implicitは呼び出すタイミングによってその都度値が評価される。
    • play frameworkではHTTPリクエストの度にRequestオブジェクトの値が変わる
  4. AkkaのActerSystemのような場合にはimplicitの値は変更可能な場合がある。(これは意味不明誰か教えてくれ)

Implicit Contextsパターンは "ボイラープレートな引数を渡す便利な方法" であり、implicit parameter の唯一使用方法である。それ以外は一般的な引数と違いはない。

Type-class Implicits

いろんな型からJsonに変換したい場合とかにパターンマッチやオーバーロードで実装もできるけど、全ての型を記述するのは不可能でなによりコンパイル時にわからないし、同名関数を定義しまくるのは汎用性がない。変換可能な型が事前にわかればコンパイルエラーで防げるし、呼ばれるべき関数が型でわかれば汎用性もあるよねという話。

ex) Jsonシリアライズ

// Jsonオブジェクトの定義
sealed trait Json
object Json{
  case class Str(s: String) extends Json
  case class Num(value: Double) extends Json
  // ... もっとたくさん
}

def convertToJson(x) // このメソッドで x をJsonオブジェクトに変換したい

このような場合 def convertToJson(x): Json の実装はどのようなパターンが考えられるだろうか。

パターンマッチ

まずは前述のパターンマッチで実装してみる。

// パターンマッチで convertToJson を実装してみる
def convertToJson(x: Any): Json = {
  x match{
    case s: String => Json.Str(s)
    case d: Double => Json.Num(d)
    case i: Int => Json.Num(i.toDouble)
    // float, short などさらに多くのケースが考えられる
  }
}

さあ。このconvertToJson()はどうだろうか...?勿論パターンマッチで実装済みの型からの変換は正しく動くだろう。しかし、未実装の型(Booleanjava.io.File)の場合はどうだろ? 実行時エラーで失敗してしまう。 現実的に考えて全ての型のパターンマッチを実装するのは不可能であるし、何より変換できない型が渡された場合はコンパイラーに止めてほしい。この問題を解決するのがType-class Implicitsパターンだ。

オーバーロード

オーバーロードを使用することで複数パターンのconvertToJson()を実装することができるかもしれない。

def convertToJson(t: String) = Json.Str(t)
def convertToJson(t: Double) = Json.Num(t)
def convertToJson(t: Int) = Json.Num(t.toDouble)

このように同名で引数の型が異なる関数を複数用意しておけば、前述の未実装の場合にコンパイルエラーが発生しない問題は回避できる。 しかし、convertToJson()のような関数を複数作成しようとした場合どうなるだろうか?

def convertToJson(t: String) = Json.Str(t)
def convertToJson(t: Double) = Json.Num(t)
def convertToJson(t: Int) = Json.Num(t.toDouble)

def convertToJsonAndPrint(t: String) = println(convertToJson(t))
def convertToJsonAndPrint(t: Double) = println(convertToJson(t))
def convertToJsonAndPrint(t: Int) = println(convertToJson(t))

def convertMultipleItemsToJson(t: Array[String]) = t.map(convertToJson)
def convertMultipleItemsToJson(t: Array[Double]) = t.map(convertToJson)
def convertMultipleItemsToJson(t: Array[Int]) = t.map(convertToJson)

こうなる。

オーバーロードは同名関数という規約があるので、少し役割を変えた関数を定義しようとした場合。その新しい関数でも全ての型を網羅するオーバーライドを記述する必要がある。これはもうさいあくである。

Type-class Implicits パターン

  • パターンマッチは全パターンを網羅することができない
  • オーバーロードは汎用性がない

ここでType-class Implicitsパターンを使用してみよう!

trait Jsonable[T]{
  def serialize(t: T): Json
}
object Jsonable{
  implicit object StringJsonable extends Jsonable[String]{
    def serialize(t: String) = Json.Str(t)
  }
  implicit object DoubleJsonable extends Jsonable[Double]{
    def serialize(t: Double) = Json.Num(t)
  }
  implicit object IntJsonable extends Jsonable[Int]{
    def serialize(t: Int) = Json.Num(t.toDouble)
  }
}

こん感じで書いておけば...

def convertToJson[T](x: T)(implicit converter: Jsonable[T]): Json = {
  converter.serialize(x)
}

このようにconvertToJson()を実装できる!!!! convertToJson()のカリー化された引数にimplicitJsonable[T]型を受け取っている。この[T]convertToJson[T]で束縛されている。

例えばxStringであった場合、このような挙動になる(っぽい)。

  1. xStringなのでJsonable[T]Jsonable[String]に束縛される
  2. Jsonable[String]implicitなのでscalaが使えるimplicitがないか捜索の度に出る
  3. まずスコープ内を探す、この場合は定義していないので見つからない
  4. 次はJsonableのコンパニオンオブジェクトを探しに行く、今回はStringJsonableを定義済みなのでここで見つける
  5. scala「お!定義済みだね!通れ!」となる

もし、Jsonable[File]のような未定義のクラスの場合。scala「だめだ!お前は!implicitが見つからないぞ!コンパイルエラーだ」となる。

さらに、前述の def convertToJsonAndPrint(t: String) = println(convertToJson(t)) のような関数を定義した場合も「お!StringからJsonに変換するimplicitがほしいんだね!StringJsonableを使いな!」となる。やったね!

Type-class Implicitsと型クラスimplicitの違い

  • Type-class Implicitsは評価する度に使用するimplicitを探しにいく、ライブラリで実装済のものを使う場合もあれば、自前で実装した物を使用する場合もある。
  • Type-class Implicitsは通常​​、データを保有しており、しばしば変更可能型クラスimplicitは純粋な関数である場合が多い。(まったく理解していない)

Derived Implicits

Type-class Implicitsでは深い階層の呼び出しも型安全に行えるよ!イエ〜イ! という話。

前述のJsonクラスでStrNumの他にJson Listも実装したいとしたらどうすれば良いだろうか?

sealed trait Json
object Json {
  case class Str(s: String) extends Json
  case class Num(value: Double) extends Json
  // ... and more
}

StrNum実装済みであるのでconvertToJson()は成功させたい。しかし、File型などの未実装の場合はconvertToJson()コンパイルエラーにして防ぎたい。

// これは実行できる
convertToJson(Seq(1, 2, 3))
convertToJson(Seq("baz", "bar", "foo"))
// これはコンパイルエラーにしたい
convertToJson(Seq(new java.io.File("."), new java.io.File("/")))

どうすればこの問題を解決できるだろうか? 答えは先ほど追加したJsonableにSeqをserializeするimplicit SeqJsonableを作成。このSeqJsonable内のSeqを[T]型に束縛してしまえばいいのだ。 きっと日本語でおkなのでコードを見てみよう!

trait Jsonable[T]{
  def serialize(t: T): Json
}
object Jsonable{
  implicit object StringJsonable extends Jsonable[String]{
    def serialize(t: String) = Json.Str(t)
  }
  implicit object DoubleJsonable extends Jsonable[Double]{
    def serialize(t: Double) = Json.Num(t)
  }
  implicit object IntJsonable extends Jsonable[Int]{
    def serialize(t: Int) = Json.Num(t.toDouble)
  }
  // このSeqJsonableを新しく追加
  implicit def SeqJsonable[T: Jsonable]: Jsonable[Seq[T]] = new Jsonable[Seq[T]]{
    def serialize(t: Seq[T]) = {
      Json.List(t.map(implicitly[Jsonable[T]].serialize):_*)
    }
  }
}

このdef serialize(t: Seq[T])[T]型の束縛のおかげで、implicit実装済みの型は呼び出せますが、未実装のjava.io.File型などは呼び出し時にコンパイルエラーになります!めでたし、めでたし。

実はDerived Implicitsパターンはこれだけではありません。どれだけネストした型でも型安全に呼び出すことができるのです! これも日本語でおkなのでコードを見ていきましょう!

// コンパイル可能
convertToJson(Seq(Seq(Seq(1, 2, 3))))
// コンパイルエラー
convertToJson(Seq(Seq(Seq(new java.io.File(".")))))

どれだけネストが深くても関係ありません。コンパイラ再帰的に型を判定し[T]型にマッチしなければコンパイルエラーに変換します。 この制約は一見面倒に感じるかもしれません。仮にJsonableが外部ライブラリだった場合、前述のようにjava.io.File型はコンパイルエラーなってしまうため実行できないからです。しかし、これはメリットでもあります。 存在しなければjava.io.File型のFileJsonableを実装すれば、Seq[File]も同様に扱うことができるのです。

// これを作ってあげる
implicit object FileJsonable extends Jsonable[java.io.File]{
    def serialize(t: java.io.File) = Json.Str(t.getAbsolutePath)
  }

// これも実行可能になる
convertToJson(Seq(Seq(Seq(new java.io.File(".")))))

このように型の束縛は実装を制限するのではなく、ユーザー側に拡張性という大きなメリットをもたらします。

Type-driving Implicits

より大きな型に変換しようとすると予想外の動きをすることがあるけど、一つの関数が引数の型に応じて戻りの型を自動で変換してくれた楽チンだよね! という話

ex) Byte -> Short -> Longに変換

// Byteで定義
val x: Byte = 123
x: Byte = 123

// Byte -> Shortに変換
val y: Short = x
y: Short = 123

// Short -> Longに変換
val z: Long = y
z: Long = 123L

これは結構便利。しかし、予想外の動きをする場合があります。

// Longの最大値
val bigLong = Long.MaxValue - 1
bigLong: Long = 9223372036854775806L

// Long -> Floatに変換
val bigFloat: Float = bigLong
bigFloat: Float = 9.223372E18F

// Float -> Longに戻す
val bigLong2: Long = bigFloat.toLong
bigLong2: Long = 9223372036854775807L

// 最初のLongと一度Floatを通したLongを比較
bigLong == bigLong2
> res40: Boolean = false

false!!!!!!!

このような事態を防ぐにはどうすれば良いでしょう?型の拡張を自動で行う関数を定義できます!

{
  def widen[T, V](x: T)(implicit widener: Widener[T, V]): V = widener.widen(x)
  class Widener[T, V](val widen: T => V)
  object Widener{
    implicit object FloatWiden extends Widener[Float, Double](_.toDouble)
    implicit object ByteWiden extends Widener[Byte, Short](_.toShort)
    implicit object ShortWiden extends Widener[Short, Int](_.toInt)
    implicit object IntWiden extends Widener[Int, Long](_.toLong)
  }
}

こんな感じで、与えられた型を自動で拡張するwiden()関数を作成します!さぁ呼び出してみましょう!

// Floatを渡すとDoubleが返ってくる
widen(1.23f: Float)
> res12: Double = 1.2300000190734863

val smallValue: Byte = 123

// Byteを渡すとShortが返ってくる
val shortValue = widen(smallValue)
> shortValue: Short = 123

// Shortを渡すとIntが返ってくる
val intValue = widen(shortValue)
> intValue: Int = 123

// Intを渡すとLongが返ってくる
val longValue = widen(intValue)
> longValue: Long = 123L

戻りの型は最初のようにscalaコンパイラが自動で判断するのではなく、Widenerに定義されているimplicitの実装で決定されます。それだけではなく、widen(longValue: Long)このように未実装な型を渡した場合はコンパイルエラーで防ぐことができます。

このDerived Implicitsは、一般的にTupleを大きなTupleに拡張する時にも用いられます。

{
  def extend[T, V, R](tuple: T, value: V)(implicit extender: Extender[T, V, R]): R = {
    extender.extend(tuple, value)
  }
}
case class Extender[T, V, R](val extend: (T, V) => R)
object Extender {
  implicit def tuple2[T1, T2, V, R] = Extender[(T1, T2), V, (T1, T2, V)] {
    case ((t1, t2), v) => (t1, t2, v)
  }
  implicit def tuple3[T1, T2, T3, V, R] = Extender[(T1, T2, T3), V, (T1, T2, T3, V)]{
    case ((t1, t2, t3), v) => (t1, t2, t3, v)
  }
  implicit def tuple4[T1, T2, T3, T4, V, R] = Extender[(T1, T2, T3, T4), V, (T1, T2, T3, T4, V)]{
    case ((t1, t2, t3, t4), v) => (t1, t2, t3, t4, v)
  }
  // ... tuple21 まで続くよ ...
}

widen()の時と同じで、戻り値の型はExtenderで定義したimplicitに依存します。結果、Tuple2Tuple3はクラス階層では互いに直接関係がありませんので、Tuple2extend()を使用することができ、scalaコンパイラは結果をTuple3であると自動的に判断します。

// Tuple2を宣言
val t = (1, "lol")
t: (Int, String) = (1, "lol")

// Tuple3に拡張
val bigger = extend(t, true)
bigger: (Int, String, Boolean) = (1, "lol", true)

// Tuple4に拡張
al evenBigger = extend(bigger, List())
evenBigger: (Int, String, Boolean, List[Nothing]) = (1, "lol", true, List())

Type-driving Implicitsパターンを使用することで、implicitパラメータのどのインスタンスが有効範囲にあるかに応じて、関数の戻り値の型をscalaコンパイラが推測する方法を制御できる。これは少しクレーバーなテクニックです。戻りの方を制御するのは気づかない可能性もありますし何でもかんでもType-driving Implicitsパターンを使用すれば良いというわけではありません。しかし、効果的に使用すれば非常に有効なパターンになります。

ex) fastparse ではこのExtenderに似た形で実装されています。

最後に

これでimplicitを使用したいくつかのデザインパターンの紹介は終わりになります。しかし、これで全てを説明しきったわけではありません。ここで説明しなかった使用方法を最後に少し言及します。

  • ShapelessAux PatternShapelessではimplicitを使用してできるほぼ全てのことが網羅されています。しかし、ここで全てに言及するのは不可能なので興味があれば除いて見てみてください。
  • implicit parametersの代わりにimplicit conversionsを使用するもの。 ex) implicit constructorsやその拡張クラスを実装するもの。(C#のものに似ている)
  • 前述のCollectionのようなものだけではなく、case classで動作するDerived Implicitsパターンも存在します。Shapelessでも実装されています。
  • Implicit Contextsはよりパワフルに、行番号やファイル名などコンパイラからさらなる情報を取得することもできます。ClassTagsourcecodeで実装されています。

以上です!!マサカリ待っています!

参考

http://www.lihaoyi.com/post/ImplicitDesignPatternsinScala.html

Rakus Meetup Tokyo #1 終わらないスクラム 行って来た

こちら行って来た

rakus.connpass.com

テックブログもあるよとのこと

tech-blog.rakus.co.jp

アジェンダ

  1. 終わらないスクラム ~楽楽精算のサービス拡大を支えるスクラム開発の取り組み
  2. テックリード(自称)としてのやっていき! ~iOSアプリ開発チームを率いて
  3. 流行の開発手法ChatOpsとは ~楽楽明細チーム/ChatOpsでルーティン作業をまるごと自動化~

終わらないスクラム

概要

ラクスでは、まだ多くの開発チームがウオーターフォール型の開発プロセスを採用していますが、 一部のチームでスクラムによるアジャイル開発に取り組んでいます。 今回は楽楽精算チームでの取り組みを紹介します。 実際にやってみると様々な問題が発生しました。問題解決に向けたアクションや取り組みを通じて得た知見、今後の課題等を事例を交えてお話しします。

登壇者

twitter.com

資料

speakerdeck.com

背景

  • 過去プロジェクトや勉強会を通してスクラムを学んだ
  • 今回のプロジェクトからスクラムを導入

フォーターフォールからスクラム

Problem

  • バーンダウンしない
    • めっちゃわかる
  • とにかく会議隊が終わらない

Try

  • タスクをなるべく細分化
  • リファインメントの徹底
  • デイリースクラムの見直し
  • カンバン
    • 物理カンバン
    • キャパシティーを表す写真を貼って、4枚以上はキャパオーバーとか測る
  • インペディメントの除去

知見

  • 与えられた役割を全うする
  • スクラムチームの役割
    1. プロダクトオーナー
    2. 開発チーム
    3. スクラムマスター
  • 役割を全うするのはどのような開発手法でも同じ
  • 短期的なスプリントを何度も繰り返すので従来の開発より振り返り・導入の機会が多い

これからのスプリントでの挑戦

感想

  • スクラムに向かって行ってる感じがしていいなって思った
  • チーム多いなって思った(10人)
  • 1年後くらいに振り返ったら全然違うチームになってそう
  • 物理カンバンのだんだん汚くなるのと付箋の粘着力問題と立ち向かっててすごいなって思った
  • バーンダウンしないのめっちゃわかる。
    • 見積もりにくそ時間かけないと正しく測れない、正しく測れないから後からタスクが湧いて来てバーンダウンがなんなら右肩上がりになる
    • 正しく見積もろうとするとそれだけで数日経つのでコードが書けない。みたいなトレードオフになる気がする

テックリード(自称)としてのやっていき!

概要

若手エースエンジニアが初めてのiOSアプリ開発でテックリードとして奮闘したお話しです。 iOSアプリ開発は、自身も初、メンバーも初、しかも短納期(3ヵ月...)。 このデスマーチを予感させる不利な条件下で、テックリードとしてどのようにチームをリーディングしたのか。 様々な事例を交えてご紹介します。

登壇者

twitter.com

背景

スクラムチーム

  • バックエンド開発チーム 3人
  • iOSチーム 3人 ← ここ
  • スクラムマスター

やっていった

  • 仕組み化
  • コード品質
  • バランスをとる

仕組み化

  • DDD
  • 詳しい人がいないのでそこまでがんばんない

コード品質

  • あえて属人化させて開発速度をあげる
  • スタイルガイドなど導入
  • CIとか外部のやつ
    • なんか聞いたことないやつだった
  • コードレビュー
    • 細かいところまでは見れないので要点を絞ってレビュー

バランスをとる

  • hubになる
  • このへん仕事のSlackきてあんま聞けなかッタ...

振り返り

  • 技術的な課題感
    • 未着手の言語だとさすがに大変そう
  • エンジニアリング頑張りたい

感想

  • チャレンジする感じいいなって思った
  • 「3ヶ月くらいで一気に作ったけどtoilで死んだ」みたいな話をいつか聞きたい
  • php/java -> swiftってどうなんだろ
    • 大変そう(小並感)
  • 挑戦して成果出してアウトプットまで繋げるの偉いなって思った(こなみ)

流行の開発手法ChatOpsとは

概要

Jenkinsの導入で自動化が進んだけど、「Jenkinsを毎回開くのは面倒」、「アカウントの作成も面倒」、「非エンジニアに使ってもらうにはちょっとハードルが高い」。 そこで導入したのがChatOps! Slack互換のチャットツール「Mattermost」でルーティン作業を丸ごと自動化しました。 利用したbotツール、システム構成、Hubotスクリプトの実例など、ノウハウを余すことなくご紹介します。

登壇者

twitter.com

資料

speakerdeck.com

背景

  • 少人数で開発~リリースまでを支える自動化の紹介

環境

  • 開発者がすきなScript書いたり
    • やばそう
  • CI/CDはJenkinsに

問題点

  • Jenkinsだるい
    • だよね

ChatOps

  1. IncomingWebHooks
  2. SlackCommands
  3. OutgoingWebhooks

hubotを使う

  • Slackコマンドのリクエストを受け取るAppサーバー立てるの怠い
  • coffeeじゃなくていい
  • npmのパッケージも作って公開した
    • すごい

gyoza.beer

  • 活用事例は若干コンプラっぽそうだったので割愛

感想

  • サービス外のコーディングは楽しい
    • わかる
  • hubotどこで動いてるんだろ
  • digdag入れたい
  • 今はhubto以外も結構あるぽい
  • Q.意識高い人が作ったものってその人がいなくなるとわからんちん問題はどうしてるのだろう
  • A.まだ引き継いだりとかっていう段階じゃない。でも最高のコードを書いてるから大丈夫!

最後に

  • 次回は来年(2020年)2月ごろだそうです。

Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました

株式会社サイバーエージェント AdTechStudio オペレーションテクノロジー事業部 iXamDrive(イグザムドライブ) でサーバーサイド・フロントエンドの開発をしている @HonMarkHunt です。

先日、 @konifar さんの記事 『B1グランプリ(Bug Bash)』を2時間やって細かいIssueを洗い出しました を拝見し素晴らしいこころみだと思ったのでiXamDriveでも挑戦して見ました!

blog.kyash.co

* iXam Driveとは?

インフィード広告の運用自動化ツール

www.cyberagent.co.jp

TL;DR

  • チーム内で触ってバグを見つけるのでは無く他チームのエンジニアにプロダクトを触ってもらいバグや分かりづらい点(UI指摘)を見つけてもらった。
  • 1時間でバグ・UI改善点が27個見つかった

Bug Bash!!

このような流れで行いました!

  1. 事前説明
  2. バグ報告
  3. 採点
  4. 景品授与

それぞれ解説していきます!

事前説明

f:id:HonMarkHunt:20180323192532j:plain:w450

まず、当日のレギュレーションを発表しました。大きく分けるとレギュレーションは以下の3つです!

  1. フロント(画面)サーバーサイドのコード(GitHubリポジトリ)フロントエンドのコード(GitHubリポジトリ)から自由にbag(と思わしきもの)UI/UX的な指摘その他なんでものを見つけて欲しい。
  2. フロント(画面) はStaging環境を使う。
    • 本番環境だと実データに影響を及ぼす危険操作ができてしまうため
  3. フロント(画面) は全機能を自由に触わるのではなく、こちらで指定した機能のみ触ってもらう。
    • 一部昨日はStaging環境でも実データに影響を及ぼす危険操作ができてしまうため

どの機能を触って貰うかミーティングで 危険操作だらけだった ことが判明したのも収穫でした。

バグ報告

  • バグと思わしきものを見つけた方は前に出てプロジェクターに繋いでバグ内容を発表してもらいました。

f:id:HonMarkHunt:20180323230637j:plain:w450

  • 想定の10倍くらいバグだらけだったのでバグ報告の行列もできてしまいました。

f:id:HonMarkHunt:20180323192728j:plain:w450

採点

  • 報告されたバグはその都度iXamDriveのメンバーで採点しました。
  • 4人のジャッジがそれぞれ1~5点までの札を持っていて、ジャッジの合計点数でもらえる景品が変わるという評価基準にしました。
  • 0点の場合は「仕様」という札をあげる。

f:id:HonMarkHunt:20180323193750p:plain:w450

(札は前日にiXamDriveメンバーで割り箸と段ボールで手作り)

f:id:HonMarkHunt:20180323192535j:plain:w350

景品授与

f:id:HonMarkHunt:20180323195056p:plain:w300

  • 報告のたびに採点してその都度点数に応じた景品を持って行って貰いました!
  • 景品用に大量の駄菓子と 机にあったいらない本 技術書を用意しました!
  • ちなみに フォームにHTMLタグをそのまま入力できる という痛恨の初歩的バグ を発見していただいた方が満点20点を叩き出しシークレット商品の iXamDriveの飲み会1回無料参加券 を獲得しました。

f:id:HonMarkHunt:20180323194847j:plain:w450

f:id:HonMarkHunt:20180323195043p:plain:w450

景品交換の様子

終了後

  • Bugは随時記録。
  • 27個ものバグが出ました!!!

f:id:HonMarkHunt:20180326121639p:plain:w400

振り返り

Bug Bashをやってみて良かったこと改善点などを書いていきます。

良かったこと

  • @konifar さんの記事を拝見してから、チームのKPT meetingでBugBash 1週間後の開催が決まりました。開催までの期間が短く、2度のmeetingを経て、他チームの方々への周知用チラシ作成、採点用札作成、景品準備など... ドタバタしながら進めていくのが文化祭みたいで楽しかったです。心なしかチーム力も向上した気がします!!
  • 参加していただいた方々にも楽しんでいただけた。参加者にはアンケートをお願いしたのですが結果がとてもよかったです!!「他チームでも開催したい。」という声があったのは嬉しかったです! f:id:HonMarkHunt:20180323201808p:plain:w400
  • issueがいっぱいできた。 ユーザーテストや単体テストと違い、自身のプロダクトを他チームのエンジニアに触ってもらう機会は中々ないと思います。エンジニアは景品欲しさに能動的攻撃を仕掛けてくるので普段は気づかないバグを発見することができました。

改善点

  • できたissueいつ消化する?問題。 issueはたくさんできたものの開発スケジュールにそんなに空きがあるはずもなく... とりあえず取り組めそうなものはバックログに追加しておいたので、一瞬手が空いた時にサッと着手できるといいかもしれないです。BugBashででたバグは当月中に解消するなどのルールを事前に決めておいてもよかったかもしれないです。
  • Bugの捜索対象をフロント(画面)サーバーサイドのコード(GitHubリポジトリ)フロントエンドのコード(GitHubリポジトリ)と3つ選出しましたが、ほとんどがフロント(画面)の指摘でした。アンケートでも「コードの指摘はそもそも仕様がわからないからハードルが高かった」とありました。機能を絞ったりしてコードも見やすいように手配すれば良かったです。

Tips

  • 発言しやすい空気を作るように心がけました。どんな報告が来てもとりあえず大声を出しました
  • 得点のフダ余ってるんで使いたい人がいたら言ってください。
  • お祭り感を出すためにわざわざチラシを作って他チームに配りました。

f:id:HonMarkHunt:20180323165458p:plain:w450

Shinjuku.LT 第18回 レポ

ShinjukuLT

www.shinjukult.tk

目次

  1. DI with Reader Monad
  2. Search Suiteのシステム構成
  3. 引っ越しを支えるかもしれない技術
  4. ツリー構造のデータをRDBで扱う
  5. 第一回 デザインクイズ
  6. Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました
  7. カイゼンジャーニー読んだよ

DI with Reader Monad

f:id:HonMarkHunt:20180324142220j:plain

登壇者

twitter.com

TL;DR

  • 関数単位の細かなDIができる
  • 評価されるまで、DIを遅延させることができる

DI

メリット

  • 疎結合

    • テストしやすい
  • 種類

    • 動的DI
    • 静的DI

SwiftでDI

  • Swinject
  • DIKit

Reader Monad

  • コードがたくさんです! f:id:HonMarkHunt:20180324142217j:plain

感想

  • 心の片隅に覚えておこう。

Search Suiteのシステム構成

f:id:HonMarkHunt:20180324143217j:plain * 全面コンプラ

http://qqqmeisi.com/wp-content/uploads/2018/02/que-1343525833.jpeg

引っ越しを支えるかもしれない技術

f:id:HonMarkHunt:20180324164826j:plain

登壇者

twitter.com

TL;DL

  • 引っ越しで支えるかもしれないいろんな情報

引っ越しにかかるコスト

  • とにかく捨てるものが多い
    • 紙の本をたくさん持ってる
  • ダンボールを送りつけると買い取ってくれるサービス

発送買取によるメリデメ

メリット * 重いものを運ばなくていい

デメリット * 引っ越しで住所が変わるとだるい

家電や粗大ゴミの破棄

  • 家電は捨てれない -> 高い
  • 粗大ゴミは低コストで捨てれるが、予約が必要
  • 地元の引っ越し屋さんの有料回収した
  • ネットのリサイクルショップは反応が遅くて微妙
  • ミニクラ 等で預かってもらっても良かった

物件探し

  • レインズ見れない(見たい)

感想

  • レインズ見たい

ツリー構造のデータをRDBで扱う

f:id:HonMarkHunt:20180324164008j:plain

登壇者

twitter.com

ツリー構造のデータで考慮すること

  • 全体参照
  • 一部参照
  • 挿入
  • 更新
  • 削除

ツリー構造をRDBでどう表現するか

  • 隣接リスト

問題点

  1. 階層が深くなると一度に引くのが大変になる
  2. 分けてクエリを発行してもクエリの発行回数が増える
  3. 挿入・更新は容易だが、削除は(末端以外だと)大変
  4. 隣接リストを用いたツリー構造は ナイーブツリー と呼ばれるアンチパターン

解決策

代わりのツリーモデルを使用する

  • ただしやや複雑になるので注意

1 . 経路列挙

経路列挙

閉包テーブル

  • メモが追いつかなかった

まとめ

  • ツリー構造の隣接リストはアンチパターンになることがある
  • 経路列挙は参照整合性が維持できない
  • 万能っぽいが、構造の難しさとデータが増える

感想

  • 全然知らなかった

第一回 デザインクイズ

f:id:HonMarkHunt:20180324164830j:plain

登壇者

twitter.com

デザインクイズ

クイズで学ぶデザイン・レイアウトの基本

クイズで学ぶデザイン・レイアウトの基本

こちらの本から抜粋してAとBのどちらのデザインが直感的に優れているのかのクイズ大会

大事なこと

  • 誰のためのデザインなのか
  • 何のためのデザインなのか

感想

  • 楽しかった!

Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました

登壇者

twitter.com

  • 僕です

資料

honmarkhunt.hatenablog.com

カイゼンジャーニー読んだよ

f:id:HonMarkHunt:20180324173756j:plain

登壇者

twitter.com

  • 小説 + 解説という面白い構成
  • リアルな状況に即したベストプラクティスに即している

感想

  • 「あなたは何をしている人なんですか?」