詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

会社は何のためにあるのか: 『ビジョナリー・カンパニー』を読んだ

ビジョナリー・カンパニー ― 時代を超える生存の原則

ビジョナリー・カンパニー ― 時代を超える生存の原則

世界を見わたして長いこと生き残り、また業界で抜きん出ている会社(ビジョナリー・カンパニー)を取りあげて、それが他の会社と違っている点は何なのか分析した本。半世紀以上も経てば世界の状況は変わり、経営者以下みな代替わりしてゆくものだけど、それでも会社として生き残った、そしてこれからもそうであろうと思える理由というのは何なのかってのを見ていく。

それは経営者の資質だとか主力製品などでは決してない。それらに依存しているような会社は、たとえその時は栄華をものにできたとしても、やがて訪れる避けられない市場の変化や人物の不在という事態には耐えられない。必要なのはすぐれた人物や製品よりも、すぐれた組織づくりの方だ。会社が作り出す究極の作品は、その会社自身なのだとこの本ではいっている。

価値観と目的

では状況の変化に耐えながら会社が生き残る秘訣は何か。それは組織の基本理念をはっきりと定め、自覚することだ。すべての決定がそれに則っておこなわれ、それ以外のことが変わってもこれだけは変わらない、そんな理念を組織の背骨に据えなければいけない。

基本理念は基本的価値観と目的とで構成される。基本的価値観は時代を越えて不変の主義であり、短期的な利益や事情で曲がらない。目的は組織の存在理由になるもの。これは組織の内側に向けた価値観と、外向きの目的と分類できると思う。

基本的価値観を会社の中心に据え、それでも時間の経過に耐えるには進歩への意欲、変化を許す風土が必要だ。そしてそれらは仕組みとして定着させなければいけない。そうでなければ時代とともに失われてしまうからだ。

本を読むと各社がどんな行動を取ってきたのかという例がたくさん載っている。なるほどこの原則に即していると思う。もちろん盲目的に従えばよいというものではないけれど、大企業とよばれるような会社が今にいたるまでどんな決断をくだしてきたのかを知るだけでも面白い。

会社は何のためにあるのか

基本理念を達成することで会社が生き残り、そのプロセスがずっと続くのならば、人びとが集まって会社を動かすことの意味は、会社の考えるような、人間ひとりではとうてい実現できない大きな善を生み出して、価値観を維持していくことにある。そしてそのことに本当に意味があるのなら、その営みはずっと続いていくだろうし、会社で働くことも無駄ではないだろう。

……と思えたので、いい本だった。別に経営者のためだけに道を示してくれるのではなく、規模や時間的なスケールの違いはあれど、ある程度以上の人間が入れ代わりながら関わる組織(それこそ、社内のチームとか)であればこの精神を活かすことはできるはずだ。

Kindle 版もあった。

ビジョナリー・カンパニー 時代を超える生存の原則

ビジョナリー・カンパニー 時代を超える生存の原則

おまけ。陳腐な表現だけど、組織は生物のようだと思ったのだった。

生命とは何か―物理的にみた生細胞 (岩波文庫)

生命とは何か―物理的にみた生細胞 (岩波文庫)

Go で reflect パッケージを使わずにジェネリックな関数を実現する

あけおめ〜。Go 界においてジェネリクスを求めるのは(今のところ)はかない望みでしかないし、もちろん重々承知していることですが、それでもときどき複雑なものを書こうとするとどうしても複数の型に対応する関数が欲しくなる。そこでこの冬休みになにかうまい方法はないかと考えて、作ってみました。

要件

今回はこんな関数が実現したくなりました。(擬似コードです)

// map[string]int とか map[string]foo.Bar を受けつける
func keys(m map[string]T) []string {
    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}

そしてこれを実現するためのアプローチを以下のように定めました。

  1. reflect パッケージを使わず、コード生成で対応する。
  2. コード生成前のコードもコンパイルできるようにする。
    • 書いている途中でも補完などが効くように。
  3. コード生成の際、対象の関数以外には手を加えない。
    • 呼び出し元はすべて同じ keys 関数の呼び出しになっていて、keys_bool(m) とか keys(wrapper(m)) とかに書き換えられない。

コンパイルできるが実行時にエラーとなるコードを、コンパイルできて実行時にもエラーとならないコードに変換する、という寸法です。実行時の安全性は変換器によって保証します。

1 を実現しているツールとして、例えば gengen があります。

方法

上記の keys 関数のシグネチャでは汎用的な型の引数を受け付けられないので interface{} に変更し、代わりに型を type switch で指定するように変更します。以下はそのスタイルで書いた完全なコードです。

// keys.go
package main

import “fmt”

type T interface{}

func keys(m interface{}) []string {
    switch m := m.(type) {
    case map[string]T:
        keys := make([]string, 0, len(m))
        for key := range m {
            keys = append(keys, key)
        }
        return keys
    default:
        panic(fmt.Sprintf("unexpected value of type %T", m))
    }
}

プレースホルダ的な型として、interface{} である T という型を定義します。(interface{} である、大文字で構成された型をプレースホルダとみなすことにします。)

これは他の場所から keys(map[string]bool{}) のように呼び出してもコンパイルできるコードになっていて、実行時に panic します。ならばあとは case 節を実際の型にあわせて増やせばよい、というわけでコード生成の出番です。この関数がどんな引数で呼び出されているかを解析し、それにあわせて case 節を増やしてやります。

実装

ここまでの話を実装したのが motemen/go-typeswitch-gen です。これに tsgen というコマンドが含まれています:

go get github.com/motemen/go-typeswitch-gen/cmd/tsgen

使い方は簡単で、汎用化したい、type switch を含んだ関数の定義されているファイルをコマンド引数として渡します。

tsgen keys.go

自動的に同じパッケージからの呼び出しを検出し、型のパターン(map[string]T など)を実際の引数の型(map[string]bool など)と突き合わせて case 節を追加した type switch を生成し、変更後のファイルの内容を標準出力に印字します。また -w を与えるとファイルを直接書き換えます。

上の keys の例だと、以下のように展開されます。

func keys(m interface{}) []string {
    switch m := m.(type) {
    case map[string]bool:
        keys := make([]string, 0, len(m))
        for key := range m {
            keys = append(keys, key)
        }
        return keys
    case map[string]T:
        keys := make([]string, 0, len(m))
        for key := range m {
            keys = append(keys, key)
        }
        return keys
    default:
        panic(fmt.Sprintf("unexpected value of type %T", m))
    }
}

またあまり出番はないかもしれませんが var x T のように書いてあった場合も var x bool として展開されます。

リポジトリの _example ディレクトリに例が同梱されているので、それを使ってみると簡単です:

% ghq get motemen/go-typeswitch-gen
% ghq look motemen/go-typeswitch-gen
% cd _example/keys
% tsgen -w keys.go
% git diff

また呼び出し元の解析のためには main 関数かテストが必要なので、対象のファイルと同一パッケージにそれらがない場合には -main 引数で指定します。

% tsgen -main github.com/motemen/foo ./util/util.go

go generate と組み合わせる

Go 1.4 から導入された go generate と組み合わせて使うのも簡単です。ファイルの先頭に以下のように書くだけ:

//go:generate tsgen -w $GOFILE

go generate すると当該のファイルが書き換わります。

既知の問題・今後の課題

import が自動的に増えない

Tio.Reader などがマッチした場合 case io.Reader: が生えますが、import "io" は追加されません。とりあえず goimports でしのげます。

コメントの位置が変になる

ベースとなる case 節にコメントを書いていた場合、生成後のコードでコメントの位置が変になります。

返り値を汎用化できない

さまざまな引数の型で同じ関数を呼び出せるようにしている都合上、返り値を汎用化させることはできません。呼び出し元の type assertion を解析するというのはできそう。 現在のところはコールバック関数を引数として与えることで任意の型を受け取れます。foreach の例:

func foreach(a interface{}, cb interface{}) {
    switch a := a.(type) {
    case []T:
        switch cb := cb.(type) {
        case func(int, T):
            for i, e := range a {
                cb(i, e)
            }
        }
}

「任意の数値型」のようなプレースホルダを定義できない

プレースホルダが interface{} だと加算が定義されていないので、数値の配列の総和、のような関数が定義できません。たぶんプレースホルダを type NumberT float64 のように定義できればよさそうだと思ってます。

終わり

tsgen で、既存の関数をベースにしてさまざまな引数の型に対して同じような処理を行える関数を生成できることを紹介しました。Go の静的解析ネタ、いろいろあるのでどこかで書くか話すかできればよいなと思ってます。

みなさま 2015 年もよろしくお願いいたします。

The Way to Go: A Thorough Introduction to the Go Programming Language (English Edition)

The Way to Go: A Thorough Introduction to the Go Programming Language (English Edition)

【想定はてブ数つき】2014 年のボツネタを一挙公開!!!

飽きっぽい性格がそうさせるのか、何かこれをやるぞ! と決めたことをやっている間に他のことを思いついてとりあえずメモっている間にどんどん積み上がっていって、果たせないままに今年も大晦日が近づいてきました。残された人生の時間と話し合って、自分が手を出すのはやめとこうと思ったアイデアをここに記しておきます。ブログに書くときの記事タイトルと想定はてブ数を併記しますので、参考にしてください。

「Perl プログラムを変数名で静的解析する」67 users

YAPC::Asia 2013 で Perl の静的解析の話が盛り上がっていて考えた。Perl のような言語に静的解析のために型アノーテーションをつけるのは煩雑で間違いを起こしやすいと考えられるけれど、静的型のない言語は名前付けが相対的に重要であることを利用して、変数名を型アノーテーション代わりに使用するというもの。例をあげると

sub get_req {
    ...
    return $req;
}

my $res = get_req(); # return $req された変数を <del>$req</del>$res で受けている!

というような齟齬を静的解析によって検出できないか、というもの。

  • やらなかった理由: Perl 最近書いてなかった。
  • ブクマ数の根拠: Perl を題材にしたのがよくなかった。Ruby か JavaScript ならもうちょっとブックマークがついたはずである。

「ソビエトロシアではブラウザがあなたをオートメーションする! または humandriver で自動テストを手動化する」44 users

ブラウザ拡張。ブラウザオートメーションの API である WebDriver の実装を人間が行う。WebDriver API では E2E テストなどで、クライアントが送る「このセレクタで得られる要素をクリックして」みたいな要求を実際にブラウザを(プログラム的に)操作することで達成する。この「プログラム的に」という部分を人間が実際に行うことで機械に使われてる感じがして面白い……だけでなく、WebDriver を使うスクリプトの検証に利用できる。

動作イメージはこんな感じ。

// 雰囲気コード
driver.findElement('button#submit').click();

たとえば E2E テストで上のように書かれていたとすると、起動中の Chrome にポップアップなどで「button#submit をクリックしてください; OK」と通知される。その通りに実行したのち OK を押すとクライアント側に操作が戻りテストが進む。

  • やらなかった理由: WebDriver API を尽くすのが大変そうだった。
  • ブクマ数の根拠: 記事タイトルがややウケだったが、実用性があまり認められず伸び悩んだ。

「機械にレシピを生成させ、食べる」229 users

機械にレシピを生成させてクックパッドに投稿し、つくれぽを教師として学習し、新たなレシピを考案する。飲んでる最中のアイデアだったけど同僚が即座にこういう文献があるね、と出してきたのが面白かった。

  • やらなかった理由: 調理中の画像を用意するのが面倒そうだった。怒られそう。
  • ブクマ数の根拠: 誰もが知っているウェブサービスと、料理という馴染み深い題材による裾野の広さでエンジニア以外にも注目された。初期のいかにもまずそうなレシピにつくれぽがつかなかったため自分たちで作って食べた、というエピソードもうけた。

「Go でジェネリクス的なことを実現する(import を利用して)」89 users

go get は Git リポジトリなりからコードを取得するので、Git サーバを自作すれば動的にコード生成することができる。そこで import 時に適当なパラメータを与えることで書き換えられたコードをダウンロードさせることができないか……と考えた。

  • やらなかった理由: Under Construction | Random とかぶった……。
  • ブクマ数の根拠: なかなか面白いアイデアだったけど、実用的にはあまり広がりがなかった。

「hubot-ramen で定時退社を促進する」51 users

hubot を使い出したので考えたやつ。Tumblr の ramen タグ からラーメン画像を探しだして、チャットに投稿する。定時とともにラーメン画像を投稿することで仕事のやる気をなくし、結果的に定時退社を促進するというソリューションの提案。

  • やらなかった理由: やりゃあ一瞬だが別にそんなにラーメン好きではないのだった。
  • ブクマ数の根拠: お前らラーメンとか好きなんだろ? という態度が透けて見え、かえって反感を誘ってしまった。

以上、総計 500 ブクマ(想定)ちかいエントリたちの紹介でした。いかがでしたか? もし気に入って実現してみた人は僕と飲みましょう! その際にはおごっていただけると幸いです。

またこのエントリを書く際にいくつかのアイデアをサルベージし、やる気を復活させることができました。みなさんもこの年末、過去のアイデアを棚卸ししてみてはいかがでしょうか?

http://www.amazon.co.jp/gp/registry/wishlist/3OFWD7Q729FDR/

はてなで一緒に働きませんか?