退職のお知らせ
今年いっぱいで株式会社オンザロード(以下OTR)を退職することになりました。 有給消化は9月からずっと半休という形で取るという形だったのはちょっと珍しいかもしれません。
OTRに入ったのは2010年1月1日ですから、11年勤めたことになります。 企業のIT化を進める会社(とか開発スタイル)のIT化が当時の自分としては全然進んでいないと感じていたところ、 「そういうこと自分で好きにやっていいよ」と誘われて入社しました。
「少なくともそういう環境が自分の手で作れるまではこの会社にいよう」と思っていたので、 実際に開発面でやりたいことを一通りやった後も留まっていたのは、ふりかえるに次の2点があったからです。
- 自分にとって理想的なチームで働ける
- 出来るなら手段は問わないという雰囲気
今回はチーム移籍ということで、1点目の利点は引き続き得られます。 2点目については、OTRにいる中である意味において成長したため、相対的に比重が小さくなっていました。
OTRに入ってからは本当にいろいろなことをやりました。 OTRはSIerなので、当然プロジェクトにアサインされはするのですが、入った経緯もあり入社直後から3~5割程度はプロジェクトに直接関係のないことを自由にやらせてもらいました。 当時はソースコードをSubversionで管理していたのですが、それをGitに移行する作業すべてを入社直後からやりました。 そのあとはJenkins(当時Hudson)を導入したり、新規参入者向けのPC選定やキッティング、社内LANの設計から実装、運用など、開発インフラの整備を継続的にやっていきました。
プロジェクトの方でも、技術力を買われて入社したからか、プロジェクトの初期段階から意見を求められることが多く、普通の中小SIerにしてはかなりの無茶を色々としました。 あるプロジェクトで設定ファイルで簡単なループをさせたい、みたいな要件があったときに、最初出てきた設計が1行ずつ解釈していくタイプのインタプリターが出てきたので、 「それやりたいならそれっぽい構文持たせてAST走査するインタプリターにしましょう!」と言ったらあっさり採用されたのでビビりました。 その時はC#だったのでNParsecで実装しました。
また、時間があれば自動化できる部分はないか考え、色々と試行錯誤しました。 その中で、Officeドキュメントの自動生成に関する技術はかなり役に立ちました。 OTRに入ってExcel方眼紙で苦しめられたことはありません。むしろ楽しんで自動化して倒しました。
この「勝手に楽しむ力」みたいなものがOTRで得られたある意味もっとも大きな部分でした。
去年、外部の大規模アジャイル開発のスクラムマスターとして働いたのですが、そこでの経験というか自分の中での気づきがありました。 そこではCOBOLの資産があり、それが大きな意味を持っていたのですが、その時点ではCOBOLなんて読めませんでした。 SMに割り当てられた環境にはVisual Studioは入っておらず、VSCodeしかありませんでした。 しかし、Windows 10にはcsc.exeがあります。 ・・・休憩時間にCOBOLのパーサーを書き始めました。
前職では「ABAPですら無理なのに、COBOLとか絶対無理だわー」と思っていたのですが、 休憩時間に勉強を兼ねてcsc.exe使ってCOBOLのパーサー書いてしかも楽しんでいる自分に気づいたのです。
このとき、「出来るなら手段は問わない雰囲気」が自分の中で急激に重要でなくなりました。 だいたいどんな環境でも勝手に楽しめそうなこと見つけて勝手にやれる、ということに気づいたのです。
この「勝手に楽しむ力」ですが、実にSIer向きの力だとは思います。 それを育ててくれたのはまず間違いなく「出来るなら手段は問わないという雰囲気」だと思っています。 その面でも、OTRは自分にとってとてもいい環境でした。
ですが、今回チーム移籍という形でOTRを離れることになりました。 今の自分にとっては、今のチームはいい環境です。 自分の好きな書籍にMy Job Went To India*1というものがあるのですが、 そのなかで「どんなバンドで演るときも一番下手なプレイヤーでいろ」というジャズギタリストの言葉あります。 この本を読んだときは確かにそうだ、と思ったものですが、今はちょっと違う意見を持っています。
少なくとも今のチームにおいて、「どの分野においても一番上手」な人なんていませんし、同時に「どの分野においても一番下手」な人もいません。 足りないところを補い合えるからこそのチームであり、「自分が一番下手」な分野がある限り大きな学びはあります。 チームという塊で見たときに「下手なこと」がある限り、チーム自体の伸びしろは残っています。 そして今のチームは(その原動力が id:kyon_mm に偏っているという問題はあれど*2 )成長を続けよう、というモチベーションが高いです。
正直な話、自分はSIer向いてると思っているし、ひとりでもやっていける自信はあります。 が、チームとしての成長は一人ではできませんし、同じようなチームをまた最初から作れる自信は全くありません。 なので、実は自分にはOTRをやめる積極的な理由はありません。 きっかけは他のメンバーの選択によるところが大きくはありますが、チームメンバーとして移籍を決めました。
次の職場についてはまた別のエントリーにする予定です。 チーム移籍活動自体の話も書く予定ではいます。
例のリストも貼っておきます。
次の職場でもがんばります!
コンピュテーション式の壊れた部分がなおりそう
F# Advent Calendar 2020の6日目のエントリーです。
今日は将来のF#の話です。
RFC FS-1087
RFC FS-1087は3つのFeatureをまとめたRFCとなっています。
- Tasks
- Resumable state machines
- Respecting Zero methods in computation expressions
ひとつめはコンピュテーション式で Task
が扱えるビルダーを追加しましょうという話です。
これは特に面白みはありません。
ふたつめはF# vNextは何が "ヤバい" のか: Monadic Programming の新時代という素晴らしいエントリーで言及されている話で、 とてもワクワクする話ですが、今回はこれの話ではありません。
みっつめの「Respecting Zero methods in computation expressions」についてです。
Respecting Zero methods in computation expressions
これは4年前に書いたComputation Expression Problemの解決です。
RFCではこうあります。
このRFC以前では、
do! expr
がコンピュテーション式の最後の位置にある場合、次のように処理された。
Return
メソッドがある場合、builder.Bind(expr, fun () -> builder.Return())
- そうでない場合、
builder.Bind(expr, fun () -> builder.Zero())
新しいルールは追加の条件を持ち、次のようになる。
Return
メソッドがあり、DefaultValue
属性が付いたZero
メソッドがない場合、builder.Bind(expr, fun () -> builder.Return())
- そうでない場合、
builder.Bind(expr, fun () -> builder.Zero())
つまり、 DefaultValue
属性付きの Zero
メソッドがある場合、今までは Return
に変換されていたのが Zero
に変換されるようになるわけです。
DefaultValue
属性を使わない場合は既存の挙動と同じになるというのはうまい回避方法だと思います。
これによって、より使いやすいコンピュテーション式ビルダーが提供できるようになるでしょう。 将来の楽しみが増えますね!
nameofについて
F# Advent Calendar 2020の5日目のエントリーです。
F#5.0で導入された nameof
は便利なんですが、いくつか制限があります。
まず、 nameof
はファーストクラス関数ではありません。
そのため、
let f = nameof
はできないx |> nameof
はできない
のような制限があります。
ひとつめは出来たら面白いですが、もし実装しようとすると関数呼び出しのたびに実体が nameof
かどうか見ないといけなくなりそうで、
おそらく実現されることはないでしょう。
ふたつめは例えばinline関数のみに限って緩和されるようなことはあるかもしれませんが、どうなんでしょうか。
他にも、昨日のエントリーで紹介した nameof
パターンにも制限があります。
nameof
は nameof symbol
と nameof<'T>
の2つの形式がありますが、 nameof
パターンで使えるのは前者のみです。
// こうは書けない let f<'a> = function | nameof<'a> -> "yes" | _ -> "no"
おまけ
nameof
はファーストクラスの関数ではないのは上で紹介した通りですが、実はシンボルではあります。
そのため、 nameof nameof
は合法で、 "nameof"
が返されます。
また、演算子もシンボルなので、 nameof (+)
のようにすれば "+"
が返されます。
面白いですね。
F# 5.0の新機能 その2
F# Advent Calendar 2020の1日目の記事であるF# 5.0の新機能で大体書かれているのですが、2つ抜けているものがあるので紹介します。
nameofパターン
nameof
の値でパターンマッチできます。
RFCにはタグを nameof
で取り出しておいて、それを元にデシリアライズする例が載っています。
// 使う型 type RecordedEvent = { EventType: string; Data: byte[] } type MyEvent = | A of AData | B of BData
// シリアライズ用関数 let serialize (e: MyEvent) : RecordedEvent = match e with | A adata -> { EventType = nameof A; Data = JsonSerializer.Serialize<AData>(adata) } | B bdata -> { EventType = nameof B; Data = JsonSerializer.Serialize<BData>(bdata) }
// デシリアライズ用関数 let deserialize (e: RecordedEvent) : MyEvent = match e.EventType with | nameof A -> A (JsonSerializer.Deserialize<AData>(e.Data)) | nameof B -> B (JsonSerializer.Deserialize<BData>(e.Data)) | t -> failwithf "Invalid EventType: %s" t
地味に便利ですね。
string関数の実装変更
string
は今まで静的に解決される型パラメーターを取るinline関数だったのですが、F#5.0からは普通の型パラメーターを取るinline関数になります。
また、今までデッドコードだったコードを有効化し、より多くの型に対するケースを追加しています。
これは例を見てもらうとびっくりするかもしれません。
// 今まではエラーだったが、F#5.0からはエラーにならない type Either<'L, 'R> = | Left of 'L | Right of 'R override this.ToString() = match this with | Left x -> string x | Right x -> string x
他にもフォーマットに "g"
を指定したのを null
に変更したりもしています。
diffを見てもらった方が分かりやすいかもしれません。
Optionalは引数に使うべきでない、という幻想について
継続渡しすると戻り値は引数になるから「Optional
は戻り値にのみ使うべき」というルールは無意味だよ、という話。
あ、そういう話ね、と分かった方はこれ以上読む必要はありません。
Mono
が Async
+ Optional
+ 例外という欲張りパック状態なのも問題ですが、それについてはまた今度(Mono<Optional<T>>
使わずに Mono<T>
を使え、という指摘があり得る。ただ、そっちもそっちで言いたいことはある、という程度)。
今回は、 Mono
は Async
くらいの意図として使っています*1。
まず、こんなメソッドがあったとします。
Mono<Optional<String>> f();
これ自体は戻り値に Optional
を使っているだけなので、「Optional
は戻り値にのみ使うべき」は守っています。
しかし、これを使う側はそうはいきません。 例えば、値が取ってこれなかった場合はログして404を返し、取れてきた場合はログして200を返すような処理を考えます。
Mono<ServerResponse> g(ServerRequest req) { return f().map(strOpt -> { if (strOpt.isEmpty()) { logger.info("str is not found."); return ServerResponse.notFound(); } var str = strOpt.get(); logger.info("str is {}.", str); return ServerResponse.ok(); }); }
ここで、map
に渡すラムダ式の引数は Optional<String>
になります。
おおっと、引数に Optional
が出てきてしまいました。
このように、Mono
など「後続処理をラムダ式として受け取る」ようなスタイルの設計においては、戻り値を引数として受け取ることになります。
これを「ラムダ式の引数は例外とする」というルールを加えてしまうと、メソッド参照によるラムダ式の置き換えが出来なくなってしまいます。
Mono<ServerResponse> g(ServerRequest req) { return f().map(this::h); } ServerResponse h(Optional<String> strOpt) { if (strOpt.isEmpty()) { logger.info("str is not found."); return ServerResponse.notFound(); } var str = strOpt.get(); logger.info("str is {}.", str); return ServerResponse.ok(); }
これは先ほどのラムダ式をメソッドとして抽出しただけで、やっていることは同じです。
ラムダ式の中身が複雑になる場合は自然にこういう書き換えはやりたくなるとおもいますが、ラムダ式を例外とするだけでは足りないことが分かります。
まぁ、 public
以外にはルールは適用しない、みたいな例外を追加するようなことは考えられますが、いっそあきらめて Optional
を引数に使わない、なんてルールやめてしまった方がいいと思います。
いやいや、複雑になったとしても取得処理を呼び出し側でやって、メソッドの引数には Optional
を許さない!という態度もなくはないです。
Mono<ServerResponse> g(ServerRequest req) { return f().map(strOpt -> { if (strOpt.isEmpty()) { logger.info("str is not found."); return ServerResponse.notFound(); } var str = strOpt.get(); logger.info("str is {}.", str); return h(str); }); } ServerResponse h(String str) { // strを使った複雑な処理 return ServerResponse.ok(); }
こんな感じですね。
値の有り無しの振り分けをラムダ式側、処理自体をメソッド側と役割を分けられるため、なくはないです。
ただ、今回の例は Optional
が1つでしたが、これが2つ、3つと増えてくると、ラムダ式内が複雑になっていくため、そこにテストを書くためにメソッド化したくなります。
その際は、やはり Optional
を引数に持つメソッドが欲しくなります。
このように、 「Optional
を引数に使わない」というルールは実践的には無意味です。捨てましょう。
「フィールドに Optional
を使わない」というルールの方はもうちょっとだけ分からなくもない理由付けがあります*2が、
こちらもすべてのクラスがシリアル化対象なわけでもないので、個人的には無意味だと思っています。
Terraformの文法について?
最近Terraformを勉強してたんですが、日本語でTerraformの文法について説明しているものが見当たらなかったのではまった(?)ところまで書いてみました。
Terraformの文法?
正確にはHCL(HashiCorp Configuration Language)の文法です。 TerraformというツールにHCLという文法に従って書かれたファイルを食わせると、 その内容(意味)をツールが読み取って実行します。
設定ファイル
設定ファイルのトップレベルに書けるのは、コメントを除くと次の3つの要素です。
- 属性
- ブロック
- 1行ブロック
設定ファイルにはこれらを複数(0個でもいい)並べられます。 例えば、ブロック、属性、属性、ブロック、1行ブロック・・・のような書き方が許されているということです。
これを公式ドキュメントでは
ConfigFile = Body; Body = (Attribute | Block | OneLineBlock)*;
ブロック
ブロックの定義はこのようになっています。
Block = Identifier (StringLit|Identifier)* "{" Newline Body "}" Newline;
さっきより複雑ですが、要は
terraform { required_version = "0.12.26" }
とか
provider "aws" { region = "ap-northeast-1" }
とか
resource "aws_instance" "hoge" { ... }
とかしているのはすべてこの「ブロック」です。
terraform
構文や provider
構文や resource
構文などと個別に定義されてはいませんでした。
ということで、HCLは大体がブロックでできています。
ブロックの中身に Body
が出てきますが、 Body
は属性かブロックか1行ブロックのいずれかの複数の並びでした。
つまり、ブロックはネスト出来ます。
# 外側のブロック resource "null_resource" "hoge" { # 内側のブロック provisioner "local-exec" { # これは属性(後述) command = "..." } }
そして個人的に衝撃だったのは、例えば resource
ブロック、こうも書けます。
resource aws_instance hoge { ... }
今まで打ってきたダブルクォートを生まれる前に消し去りたい。
もう一度ブロックの定義を見てみましょう。
Block = Identifier (StringLit|Identifier)* "{" Newline Body "}" Newline;
(StringLit|Identifier)*
となっている部分に注目してほしいのですが、
これは「(文字列リテラルか識別子)が0個以上並ぶ」ことを意味しています。
provider "aws" { region = "ap-northeast-1" }
この "aws"
が文字列リテラルで、
provider aws { region = "ap-northeast-1" }
こっちの aws
が識別子です(provider
も識別子ですね)。
そしてこの部分を文字列リテラルで書いても識別子で書いても同じ意味です。
もっと早く公式の仕様を確認すべきでした。
他に面白い構文も特にないしやる気も尽きたので、ここまでにしておきます。 Terraform、ダブルクォートがだるいと思って避けている人もいるんじゃないかなと思うんですが、 別に書かなくていい場所になぜか書いている人が多いだけだよ、ということは広まってほしいですね。 公式のExample Usageがダブルクォート付きで書いているので、 お作法としては書くべきなのかもしれないですが面倒だし芋っぽいので自分は書かないことにします。
余談
Terraformは独自言語にもかかわらず、構文自体の説明は驚くほど少なくてびっくりします。 構文がシンプルなので説明不要でなんとなく使えるというのもあるんでしょうね。
CloudFormationはその点、文法はYAMLです、で説明がほぼ終わるのは利点ですよね。 関数とか独自機能もありますけど、YAML的にはvalidを保ったままの拡張なので、YAMLさえ知っていれば構文について覚えることは少なくて済みます*2。
WSLへのOchaCamlのインストール方法
基本的にはOchaCamlの公式ページに従えばいいんですが、環境をWSL(Ubuntu)に固定し、日本語でまとめておきます。
準備
makeとgccが入っていない場合はインストールしておきます。
sudo apt install make gcc
インストール方法
ホームディレクトリで作業します。
cd wget http://caml.inria.fr/pub/distrib/caml-light-0.75/cl75unix.tar.gz tar xpfvz cl75unix.tar.gz wget http://pllab.is.ocha.ac.jp/~asai/OchaCaml/download/OchaCaml.tar.gz tar xpfvz OchaCaml.tar.gz patch -p0 < OchaCaml/OchaCaml.diff cd cl75/src sed -i -e 's/ -no-cpp-precomp$//' Makefile make configure make world
起動方法
cd ./OchaCaml/ochacaml
これでREPLが立ち上がります。