Magnolia Tech

いつもコードのことばかり考えている人のために。

ISUCON11に参加した

isucon.net

一人参加だったとはいえ、かなりのいきあたりばったり感で進めてよくなかったなーと後で反省。

それでもDBサーバを別サーバへ分離したり、インデックスを貼って行ったり、クエリのやり方を変えたりする程度でも初回ベンチより倍以上のスコアが出てちょっと嬉しかった。

事前の準備が全然できていなかったので、直前に『Software Design 7月号』を片手にISUCON10予選問題を少しやった程度で突入してしまったけど、過去問を2〜3回解いてからチャレンジしていればまた手数も変わったんだろうなぁって思った。

そういう意味では基本に忠実にやるだけでもスコアが上がって、その先に色々な方法でスコアを上げる選択肢が有る、奥の深さがISUCONの魅力なんじゃないかって思った。素直にリアルな課題に向き合った結果がフィードバックされて問題が作られているのがよく分かる。だからみんなISUCONの問題は解いた方がいいって言うんだなって改めて実感した。

あと、やっぱり8時間という時間が全然短いし、緊張感が凄まじい。途中全然他のことに気が取られなかった。

また、問題を解く環境としてのホスタピリティというか、気持ちよく、スムーズに競技に参加できるようにするためも環境整備が凄まじくて、競技環境のセットアップとかマジでよく作り込まれている。あの運営ノウハウだけでも凄まじい価値が有る。

本戦にはほど遠い点数だったけど、ぜひまた参加したいな。

計画の解像度を上げていく

2021/8/15: 最初の言説のところ、微妙に何が何だかって記述だったので少し見直しました。


システム開発は最後まで分からない、常に変更が続くのだ!だから詳細な事前の計画は無意味、やってみないと分からない!」という言説があり、まぁそうだなーと思う反面、個人的な活動ならばそれでいいけど、それなりのステークホルダーが存在する事業の場合は、「見通し」が無いと投資できませんって話にもなる。

結局計画である以上は常に未来の不確定さに、一定のリスクがあるわけで、「完全完璧な計画を求める人」も、「やってみないと分からないと言う人」も、どっちもその不確実さが自分に降りかかることを忌避して、リスクコントロールをしない人にも見える。

アジャイルは無計画ではなく、「計画をするタイミング、詳細度」をコントロールする方法論だと理解していて、「Perlがスコープをコントロールする言語」なのと同じくらい直感的でなく、一回やってみないと分からないけど、非常に実務的な発想だな、とも思う。


少し話は変わって、「その日やることをその日計画する」のと、「その日やることを前日までに計画する」では同じ計画でも意味合いが違ってくると思っていて、「その日やることをその日計画する」というのは得手して「やれることをやる」に陥りがちになる。一方で「その日やることを前日までに計画する」は、「やるべきことをやる」がやり易いやり方だと理解している。

それは計画に対する解像度の違いで、「やれることをやる」の場合、ゴールに向かうために、抜き差しする要素が限定されてしまうのに対して、「やるべきことやる」の場合、要素の抜き差しができるからだ。つまり、「余計なことはやらない」。

「余計なことはやらない」と言っても、単純に「止めちゃえばよかった」なんてことはあんまり無くて、「止めるなりの準備や、調整」が有って初めてやめられる。「止めるために新たにやらないといけないこと」が出てくるわけで。


計画の粒度、どこまでできていれば納得するかは、極めて個人の経験や資質、組織文化に由来することなので、絶対的な正解が有るわけではないけど、「計画の解像度を上げていく」という行為に意識的になっていくといいんじゃないかと思った全然まとまりの無い話。


追加

Scala3学習メモ: Scala3におけるType Lambda

Scala2におけるType Lambdaについては、下記のブログエントリが詳しい。

underscore.io

最初に、型パラメータの数を一つとして定義していると、型パラメータを二つ取る物を渡せない。

scala> trait Functor[F[_]]
trait Functor

scala> type F1 = Functor[Option]
type F1

scala> type F2 = Functor[List]
type F2

scala> type F3 = Functor[Map]
                         ^
       error: Map takes 2 type parameters, expected: 1

Mapが取る二つの型のうち、一つが決まれば、残るは一つなので大丈夫。

scala> type IntKeyMap[A] = Map[Int, A]
type IntKeyMap

scala> type F3 = Functor[IntKeyMap]
type F3

ただし、いちいち途中の階層の型を定義するのは面倒くさい。

そこで、無名の型を定義して、その中で一つの型を確定させ、その型を返す方法が発明された。これなら1行で済むし、余計な命名も不要になる。

scala> type F5 = Functor[({ type T[A] = Map[Int, A] })#T]
type F5

でも何をやっているのかわかりづらい。

そこで、Scala3には専用の構文(=>>)が導入された。だいぶ謎記号が減ったScalaだけど、ここに来て一つ増えた。

scala> type F6 = Functor[ [A] =>> Map[Int, A] ]
// defined alias type F6 = Functor[[A] =>> Map[Int, A]]

Scala2の時のtype lambdaの書き方をすると、対応が分かりやすい。

scala> type F7 = Functor[({ type T[A] = Map[Int, A] })#T]
// defined alias type F7 = Functor[[A] =>> Map[Int, A]]

型のカリー化もできるそうだ。

type TL = [X] =>> [Y] =>> (X, Y)

型パラメータが得られば、こんな指定もできる。

scala> def foo[A[_,_],B](functor: Functor[({type AB[C] = A[B,C]})#AB]) = ???
def foo[A[_$3, _$4], B](functor: Functor[[C] =>> A[B, C]]): Nothing

scala> def foo[A[_,_],B](functor: Functor[ [C] =>> A[B, C]]) = ???
def foo[A[_$1, _$2], B](functor: Functor[[C] =>> A[B, C]]): Nothing

ライブラリを作る人でなければ、このような汎用性のための機能はいらないかもしれないけど。

あと、これらのコードを動かしている最中に、aliasの挙動がよく分からなくなったけど、良い記事を見つけた。

blog.shibayu36.org

夏休みのお供に『アンダースタンディング コンピュテーション』

技術書なんて必要なところを拾い読みするか、読書会でいろんな人の考え方や経験を知るきっかけに使うもの...慣れてくるとそんな感覚になってきますが、せっかくの夏休みなので初心に帰って、全部頭から再度まで読み切って、かつコードの写経もやってみて…というのをじっくりやるのもいいかもしれません。

そんな時には、できれば改めて基礎からじっくり学ぶ系の本が良いので、『アンダースタンディング コンピュテーション』などは如何でしょうか。

出版されたのは2014年ともう時間もけっこう経っていますが、「そもそもプログラミング言語って何だ?」「計算機って何だ?」「っていうか、ここで言う計算って何だ?」という問いかけに丁寧に答えてくれる1冊になっています。つまり、普段なかなか真正面から学ぶ機会のない、コンピューサイエンスで学ぶことのうちの一つですね。

特に前半の、Rubyを使ってプログラミング言語を実装する部分は単に写経するだけでも、構造が見えて学びが多いです。というか、絶対に写経した方がいいですね、わかった気になって単に読み進めていくと、「あれ、全然分からなくなった...」という瞬間がやっていきます。

中盤の、オートマトンから正規表現も、普段なにげなく使っている正規表現がどのような理論の上で構築されているのか、実際に動くプログラムで実感することができます。

後半の「第II部 計算と計算可能性」はかみごたえが有るというか、毎回自分も途中で挫折してしまうのですが、じっくり読んで理解を進めていく機会には、このくらいの難易度が欲しいですね。

というわけで、残念ながら自由に外にも出かけられない夏休み、クーラーの効いた部屋でじっくり技術書を、「考え、実行しながら」読み進めて、考えを深めるのはいかがでしょうか?

Scala3学習メモ: Intersection型

Scalaではclassの定義時にtraitをmix-inできる。

scala> trait A
// defined trait A

scala> class B extends A
// defined class B

scala> val o1 = new B
val o1: B = B@36b53f08

この時、変数の型はBと推論されている。

また、オブジェクトの生成時にもmix-inできる。この時の型は先ほどと異なる結果となる。

scala> class C
// defined class C

scala> val o2 = new C with A
val o2: C & A = anon$1@5486ee92

この時の型はScala3から導入されたIntersection型により、変数はCとAを同時に持つ型として推論されている。

変数定義だけでなく、当然引数の型の宣言でも使える。

scala> def method(obj: A & C) = println(obj.toString)
def method(obj: A & C): Unit

scala> method(o2)
repl$.rs$line$6$$anon$1@5486ee92

Intersectionには型の継承関係が整理されているので、詳しくは公式ドキュメントを見ると良い。


Scala2の時は、Compound Typeという型で推論されていた。

scala> trait A
defined trait A

scala> class C
defined class C

scala> val o = new C with A
o: C with A = $anon$1@ecd379a

もちろん明示的に指定できる。

scala> val o2: C with A = new C with A
o2: C with A = $anon$1@22c7ef94

この変数の型におけるwithを使ったCompound Typeの記法は将来は廃止される予定であるとされている。

Scala3で明示的に指定してもIntersectionで置き換えられている。

scala> val o4: A with C = new C with A
val o4: A & C = anon$1@799971ac

Scala3学習メモ: newキーワードが不要になった

case classではない、通常のクラスでもapplyメソッドを持つコンパニオンオブジェクトが自動生成されるようになったので、newキーワードを使わなくてもオブジェクト生成ができるようになった。

scala> case class Person(name: String, age: Int)
// defined case class Person

scala> Person("Mike", 42)
val res0: Person = Person(Mike,42)

scala> class Company(name: String)
// defined class Company

scala> Company("ANAHEIM ELECTRONICS")
val res1: Company = Company@3253d771

toStringメソッドが再定義されているわけではないので、オブジェクトのダンプの見え方は従来と変わらない。