Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

March 25 2017

「隠すことによるセキュリティ」が何をもたらすか - remark

例の Vault 7 絡みの話。

Cisco は相変わらず「遅い」なぁ。 まぁほかのネットワーク機器を販売している企業も似たり寄ったりなのだが。

それはともかく,今回の Cisco 製品の脆弱性は telnet 絡みのもののようだ。

CMPとは、クラスターを構成する別の機器に、TELNETを使ってメッセージやコマンドを送信するためのプロトコル。今回見つかったCMPの脆弱性を突くと、攻撃者はアクセス権限のないシスコ製品にTELNETで接続できる。その結果、その製品を乗っ取ったり、製品上でウイルスを実行したりすることが可能になる。
via CIAの機密文書で発覚、シスコ製品300種類にパッチ提供未定の危険な脆弱性

言うまでもないが,もはや telnet は使ってはいけない。 可能な限り無効にするべき。

パッチ提供前の対策として同社が挙げているのが、IOS/IOS XEでTELNETを無効にすること。これにより、脆弱性がある機器でも攻撃されることを防げる。TELNETの代わりには、SSHを利用するよう推奨している。また、TELNETを無効にできない環境では、アクセス制御で対応するよう呼びかけている。
via CIAの機密文書で発覚、シスコ製品300種類にパッチ提供未定の危険な脆弱性

その上で ssh を使えばいいのだが ssh でもパスワード認証は比較的破られやすいことが分かっているので必ず公開鍵暗号を使った認証を使うこと。 ついでに ssh の version 1 は使わないこと。

Vault 7 では脆弱性情報について PoC (Proof of Concept) や exploit code などの詳細情報がなく解析に時間がかかっているようだ1

ところが今回のケースでは、CIAから流出したとする機密文書Vault 7から脆弱性の存在が判明した。このためシスコは、詳細な情報やエクスプロイトを入手していない可能性がある。
実際、シスコの公式ブログには、「(今回の脆弱性に関連する)ツールやマルウエアは公表されていないので、シスコが取れるアクションは限られている。より多くの情報を入手するまでは、脆弱性ハンドリングの観点でシスコができることはほとんどない」としている。
via CIAの機密文書で発覚、シスコ製品300種類にパッチ提供未定の危険な脆弱性

WikiLeaks 側は製品のベンダ企業に対して脆弱性の一般公開まで90日間の猶予を設けているらしい(予告なしに公開はしないということ)。

WikiLeaks included a document in the email, requesting the companies to sign off on a series of conditions before being able to receive the actual technical details to deploy patches, according to sources. It's unclear what the conditions are, but a source mentioned a 90-day disclosure deadline, which would compel companies to commit to issuing a patch within three months.
via WikiLeaks Won’t Tell Tech Companies How to Patch CIA Zero-Days Until Its Demands Are Met

90日の猶予期間というのは Google などでもやっているので特に無理筋な設定ではないと思うが Cisco みたいな企業だと厳しいかもしれない。 一方で Android や iOS などでは発覚した脆弱性の殆んどは修正済みだったらしい。 そういった脆弱性情報の解析・対処の能力の違いはありそうである。

この問題の根源は CIA などの諜報機関が脆弱性情報を「悪用」するために秘匿していたことである。 よく「隠すことによるセキュリティはダメ」と言われるが,目的はどうあれ脆弱性情報を秘匿することがどういう結果をもたらすか身に染みたのではないだろうか。

Honestly, at this point the CIA should do the right thing and disclose all the vulnerabilities to the companies. They're burned as CIA attack tools. I have every confidence that Russia, China, and several other countries can hack WikiLeaks and get their hands on a copy. By now, their primary value is for defense. The CIA should bypass WikiLeaks and get the vulnerabilities fixed as soon as possible.
via WikiLeaks Not Disclosing CIA-Hoarded Vulnerabilities to Companies

ブックマーク


  1. Vault 7 のリーク元がロシアということもあって情報自体の信憑性を確認する必要もある。 [return]

March 24 2017

オープン戦なので勝ち負けをとやかく言うべきじゃないんだろうけど、この時期にこれはそろそろどうなんだろう。

March 23 2017

人工知能に「常識」は必要なのか? - remark

「常識」は「知能(intelligence)」というより「知性(intellect)」に近い。 果たして人工知能(artificial intelligence)に「常識」は必要なのか。

  1. 機械と「トロッコ問題」
  2. 人工知能に「常識」は必要なのか?

機械と「トロッコ問題」

「トロッコ問題」については以前に言及した。

「トロッコ問題」のポイントは「正解が存在しない」ことにある。 ゆえにヒトは葛藤し(どちらを選んでも)後悔するのだ。

宗教臭い言い方をするなら葛藤は「罪」であり後悔は「罰」である。 社会的動物である私たちヒトは常に社会に対して罪と罰を背負わされ続ける。 一方で,(社会的に1)「公正」であり「正義」であると信じ切れるなら,それが自爆テロでも人類殲滅でも,罪と感じることはないだろう。

機械は葛藤も後悔もしない。 内部構造がどうであれ入力に対して必ず何らかの出力を行うよう定められている。 その意味で機械に「トロッコ問題」は存在しない。 言い方を変えるなら「葛藤や後悔は機械が負う責務ではない」ということだ。

人工知能に「常識」は必要なのか?

「常識」というのはヒトが社会的動物であることと密接な関係がある。 そもそも「公平かつ正当な判断」という考え方からしてそうだ。 「公平」も「公正」も社会の中でこそ意味を成す。

「常識」というのは社会に対する個人(identity)を固定するための基盤であり,同時に個人の想像力を制限する枷でもある。 人は想像を絶するものは想像できない。 その想像力の地平線こそが「常識」というやつである。

ならば問おう。 果たして人工知能に「常識」は必要なのか? と。 「常識」を刷り込まれ「想像を絶するものを想像できない」人工知能の価値とは如何ほどか。

巷では「人工知能が人の仕事を奪う」みたいな言説が流布されているけど,機械の本領は人の仕事を肩代わりすることではない。
via ゲーム AI の本領

ブックマーク


  1. 社会的にというか「神の教えの下に」とでも言うべきか。 [return]
肉玉そば(右)と肉玉うどん(左)。そばは細麺
ニラ玉いえー
しいたけバター
ゲソバターからスタート

March 22 2017

同点になってる。
グルメシャッター街。水曜日なんだけど、今日。

March 21 2017

HTTPS 通信監視機器のリスク - remark

2015年の CERT/CC ブログ記事 “The Risks of SSL Inspection” に関する注意喚起が今更ながら出ている。

「HTTPS 通信監視機器」というのは,ぶっちゃけていうと, HTTPS 暗号通信1 に「中間者攻撃(man-in-the-middle attack)」を仕掛けて通信を傍受し malware 等を検出・排除する「セキュリティ製品」である。

HTTPS 通信監視機器のいくつかにはセキュリティ上の問題が存在する。 “The Risks of SSL Inspection” から抜き出してみよう。

  1. Incomplete validation of upstream certificate validity
  2. Not conveying validation of upstream certificate to the client
  3. Overloading of certificate Canonical Name (CN) field
  4. Use of the application layer to convey certificate validity
  5. Use of a User-Agent HTTP header to determine when to validate a certificate
  6. Communication before warning
  7. Same root CA certificate

これらの問題があると推測される製品のリストが “The Risks of SSL Inspection” に挙がっているので該当者は確認してみるとよいだろう。 また以下のサイトからも確認できる。

The Risks of SSL Inspection” では以下のように結論付けている。

SSL and TLS do not provide the level of end-to-end security that users may expect. Even in absence of SSL inspection, there are problems with how well browsers are conveying SSL information to users. The fact that "SSL inspection" is a phrase that exists, should be a blazing red flag that what you think SSL is doing for you is fundamentally broken.
via The Risks of SSL Inspection

以前も書いたが,HTTPS 通信監視機器(あるいは HTTPS Deep Inspection)の存在自体がインターネットの “End to End” 原則を崩すものであり,ひいては「ネットの中立性」に楔を入れるものである。 しかし「馬も鹿も暗号化する時代」にこの原則は風前の灯である。 たとえば CMS の面倒すらろくすっぽ見られないユーザが「うちも Let’s la Encrypt」とか言い出して脆弱性だらけのサイトを暗号化したらどうなるのか。

ネットワーク・セキュリティ専門家から企業あるいは私たち個人に至るまで,場当たりな対処に満足するのではなく,この「現実」にきちんと向き合うべきだと思うのだが,どうだろう。

ブックマーク

参考図書

photo
暗号技術入門 第3版 秘密の国のアリス
結城 浩
SBクリエイティブ 2015-08-25
評価

自作エミュレータで学ぶx86アーキテクチャ コンピュータが動く仕組みを徹底理解! 数学ガールの秘密ノート/丸い三角関数 数学ガールの秘密ノート/微分を追いかけて 情報セキュリティ白書2015: サイバーセキュリティ新時代:あらゆる変化へ柔軟な対応を 数学ガールの秘密ノート/数列の広場

SHA-3 や Bitcoin/Blockchain など新しい知見や技術要素を大幅追加。暗号技術を使うだけならこれ1冊でとりあえず無問題。

reviewed by Spiegel on 2015-09-20 (powered by G-Tools)


  1. 念のため簡単に説明しておくと, HTTPS (Hypertext Transfer Protocol Secure) 暗号通信は WWW (World Wide Web) におけるクライアント-サーバ間の通信経路を暗号化する仕組みである。具体的には TLS (Transport Layer Security) 等のプロトコルを用い公開鍵暗号方式を使ってセッション鍵を生成する。また公開鍵暗号方式の公開鍵は X.509 方式の公開鍵基盤(Public Key Infrastructure; PKI)によって管理される。 [return]

欲しいものリスト2017年3月版 - remark

読みたいんだけど(ゆっくり読む暇がなくて)買わずに放置している本が大分溜まってきてるんだけど,今回はそれらを列挙して「欲しいものリスト2017年3月版」とする。


今年に入ったら買おうと思ってたんだけど,年末年始に買った本がいまだに読み終わらない1

内容についてはあまり期待してないけど,情報収集の一環として読んでおきたい感じ? 優先度低目。 できれば Kindle 版を待ちたい。

いや,そろそろ Swift もちゃんと勉強しなきゃなぁ,って感じで。 こちらも優先度低目。

この辺は純粋に「面白そうなので読んでみたい本」。 最後のは広島の「しおまち書房」によるもので2017年4月発売予定。 私は英語不得手なので是非読んでみたいと思う。

こちらは「とりあえず買った」のだが積読中。 早めに読んでおきたい。


  1. つか,そろそろ Bitcoin や Blockchain 自体はいいかな,って気がしてる。私が田舎に住んでいるというのもあると思うが,正直これらに未来を感じない。派生技術はいろいろ面白いものだ出るだろうけど。 [return]

March 19 2017

実家からの支援物資に入ってた。「国内で丁寧に焼きました」のコピーが笑かす。ちなみに原料の海苔は韓国産。よく分からんw 製造者は大丸商事。広島市佐伯区らしい。

March 18 2017

にごり酒入れてみた

March 14 2017

配列と Slice - golang

以下の記事を見て思わず膝を打った。

なるほど! こういう風に説明すればいいのか。 というわけで,私も便乗してみる。 あくまでも私のための覚え書である。

配列は常に「値」である

まずはこんなコードを書いてみる。

package main

import "fmt"

type Array4 [4]int8

func main() {
	ary := Array4{0, 1, 2, 3}
	fmt.Printf("ary = %p\n", &ary)
	for i := 0; i < len(ary); i++ {
		fmt.Printf("%p: %v\n", &ary[i], ary[i])
	}
}

[4]int8 が「型」であることを意識してもらうために敢えて Array4 という型を宣言している。 実行結果は以下の通り。

ary = 0x1040a124
0x1040a124: 0
0x1040a125: 1
0x1040a126: 2
0x1040a127: 3

まぁ分かりやすいよね。 今度はダンプ表示部分を別関数にしてみる。

package main

import "fmt"

type Array4 [4]int8

func dump(ary Array4) {
	fmt.Printf("ary(dump) = %p\n", &ary)
	for i := 0; i < len(ary); i++ {
		fmt.Printf("%p: %v\n", &ary[i], ary[i])
	}
}

func main() {
	ary := Array4{0, 1, 2, 3}
	fmt.Printf("ary(org) = %p\n", &ary)
	dump(ary)
}

このコードの実行結果は以下の通り。

ary(org) = 0x1040a124
ary(dump) = 0x1040a128
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3

dump() 関数の引数として渡される配列がオリジナルのものと異なることが分かるだろう。 Go 言語では関数の引数は原則として「値渡し(call by value)」であるため,配列を渡す場合でも配列のコピーを作って渡すことになる。 配列を値渡しではなく「参照渡し(call by reference)」にしたい場合はポインタを使う。

package main

import "fmt"

type Array4 [4]int8

func dump(ary *Array4) {
	fmt.Printf("ary(dump) = %p\n", ary)
	for i := 0; i < len(ary); i++ {
		fmt.Printf("%p: %v\n", &ary[i], ary[i])
	}
}

func main() {
	ary := Array4{0, 1, 2, 3}
	fmt.Printf("ary(org) = %p\n", &ary)
	dump(&ary)
}

結果は以下の通り。

ary(org) = 0x1040a124
ary(dump) = 0x1040a124
0x1040a124: 0
0x1040a125: 1
0x1040a126: 2
0x1040a127: 3

Slice は配列への参照である

次は配列を slice に置き換えてみよう。

package main

import "fmt"

func dump(slc []int8) {
	fmt.Printf("slc(dump) = %p\n", slc)
	for i := 0; i < len(slc); i++ {
		fmt.Printf("%p: %v\n", &slc[i], slc[i])
	}
}

func main() {
	slc := []int8{0, 1, 2, 3}
	fmt.Printf("slc(org) = %p\n", slc)
	dump(slc)
}

配列の場合の記述の違いが分かるだろうか。 この場合 slc には配列 {0, 1, 2, 3} へのポインタがセットされる。 したがって dump() 関数の引数には(見かけ上)配列 {0, 1, 2, 3} への参照がセットされていることになる。

slc(org) = 0x1040a124
slc(dump) = 0x1040a124
0x1040a124: 0
0x1040a125: 1
0x1040a126: 2
0x1040a127: 3

では応用として今度はこんなコードを考えてみよう。

package main

import "fmt"

type Array4 [4]int8

func dumpA(ary Array4) {
	fmt.Printf("ary(dumpA) = %p\n", &ary)
	for i := 0; i < len(ary); i++ {
		fmt.Printf("%p: %v\n", &ary[i], ary[i])
	}
}

func dumpS(slc []int8) {
	fmt.Printf("slc(dumpS) = %p\n", slc)
	for i := 0; i < len(slc); i++ {
		fmt.Printf("%p: %v\n", &slc[i], slc[i])
	}
}

func main() {
	ary := Array4{0, 1, 2, 3}
	fmt.Printf("ary(org) = %p\n", &ary)
	dumpA(ary)
	slc := ary[:]
	dumpS(slc)
}

slc := ary[:] で配列 aryslice slc にキャストされているのがポイントである。 実行結果は以下の通り。

ary(org) = 0x1040a124
ary(dumpA) = 0x1040a128
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
slc(dumpS) = 0x1040a124
0x1040a124: 0
0x1040a125: 1
0x1040a126: 2
0x1040a127: 3

配列 ary のオリジナルのポインタ値がそのまま slc の値になっているのが分かると思う。 この「slice は配列への参照である」ということを踏まえると,こんな面白いコードも書ける。

package main

import "fmt"

type Array4 [4]int8

func dumpA(ary Array4) {
	fmt.Printf("ary(dumpA) = %p\n", &ary)
	for i := 0; i < len(ary); i++ {
		fmt.Printf("%p: %v\n", &ary[i], ary[i])
	}
}

func dumpS(slc []int8) {
	fmt.Printf("pointer(dumpS) = %p\n", slc)
	fmt.Printf("size(dumpS) = %v\n", len(slc))
	fmt.Printf("capacity(dumpS) = %v\n", cap(slc))
	for i := 0; i < len(slc); i++ {
		fmt.Printf("%p: %v\n", &slc[i], slc[i])
	}
}

func main() {
	ary := Array4{0, 1, 2, 3}
	fmt.Printf("ary(org) = %p\n", &ary)
	dumpA(ary)
	slc1 := ary[0:2]
	dumpS(slc1)
	slc2 := slc1[0:4]
	dumpS(slc2)
}

サイズ 2 の slc1 からサイズ4の slc2 を取得しているのがポイント。 このコードの実行結果は以下の通り。

ary(org) = 0x1040a124
ary(dumpA) = 0x1040a128
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
pointer(dumpS) = 0x1040a124
size(dumpS) = 2
capacity(dumpS) = 4
0x1040a124: 0
0x1040a125: 1
pointer(dumpS) = 0x1040a124
size(dumpS) = 4
capacity(dumpS) = 4
0x1040a124: 0
0x1040a125: 1
0x1040a126: 2
0x1040a127: 3

実は slice は配列に対してポインタとサイズと容量の3つの属性を持つオブジェクトである。 上述のコードでは配列 ary を反映し, slc1 の容量が4となるため slc2 ではサイズ4の slice が作れるわけだ。

たとえば slc1 := ary[2:4] と書き換えると

package main

import "fmt"

type Array4 [4]int8

func dumpA(ary Array4) {
	fmt.Printf("ary(dumpA) = %p\n", &ary)
	for i := 0; i < len(ary); i++ {
		fmt.Printf("%p: %v\n", &ary[i], ary[i])
	}
}

func dumpS(slc []int8) {
	fmt.Printf("pointer(dumpS) = %p\n", slc)
	fmt.Printf("size(dumpS) = %v\n", len(slc))
	fmt.Printf("capacity(dumpS) = %v\n", cap(slc))
	for i := 0; i < len(slc); i++ {
		fmt.Printf("%p: %v\n", &slc[i], slc[i])
	}
}

func main() {
	ary := Array4{0, 1, 2, 3}
	fmt.Printf("ary(org) = %p\n", &ary)
	dumpA(ary)
	slc1 := ary[2:4]
	dumpS(slc1)
	slc2 := slc1[0:4]
	dumpS(slc2)
}

slc1 の容量が変わるため,以下のように実行時 panic になる。

ary(org) = 0x1040a124
ary(dumpA) = 0x1040a128
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
pointer(dumpS) = 0x1040a126
size(dumpS) = 2
capacity(dumpS) = 2
0x1040a126: 2
0x1040a127: 3
panic: runtime error: slice bounds out of range

このように配列と slice の関係が分かると append() 関数の挙動も理解しやすくなる。

ところで先ほど slice は「ポインタとサイズと容量の3つの属性を持つオブジェクト」と書いた。 つまり厳密に言えば,関数の引数に slice をセットするということは「ポインタとサイズと容量の3つの属性を持つオブジェクト」を値渡しでセットしているということになる。 たとえば以下のようなコードを考える。

package main

import "fmt"

type Array4 [4]int8

func dumpS(slc []int8) {
	fmt.Printf("pointer(dumpS) = %p\n", slc)
	fmt.Printf("size(dumpS) = %v\n", len(slc))
	fmt.Printf("capacity(dumpS) = %v\n", cap(slc))
	for i := 0; i < len(slc); i++ {
		fmt.Printf("%p: %v\n", &slc[i], slc[i])
	}
}

func addS(slc []int8, e int8) {
	slc = append(slc, e)
	dumpS(slc)
}

func main() {
	slc := make([]int8, 4, 8)
	slc[0] = 0
	slc[1] = 1
	slc[2] = 2
	slc[3] = 3
	dumpS(slc)
	addS(slc, 4)
	dumpS(slc)
	slc2 := slc[0:5]
	dumpS(slc2)
}

杜撰なコードではあるが,サイズ4容量8の配列に5番目の要素を append() しても内部の配列そのものは更新されないため動きとしては問題ないように見える。 しかし実際には以下のような実行結果になる。

pointer(dumpS) = 0x1040a128
size(dumpS) = 4
capacity(dumpS) = 8
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
pointer(dumpS) = 0x1040a128
size(dumpS) = 5
capacity(dumpS) = 8
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
0x1040a12c: 4
pointer(dumpS) = 0x1040a128
size(dumpS) = 4
capacity(dumpS) = 8
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
pointer(dumpS) = 0x1040a128
size(dumpS) = 5
capacity(dumpS) = 8
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
0x1040a12c: 4

つまり addS() 関数に渡す slc は値渡しなので addS() 関数内で slc のサイズが変わっても関数の呼び出し元には反映されないことになる(配列自体には値がセットされている)。 append() 関数実行後は必ず状態が変わるため正しく slice の「値」を更新する必要がある。

配列の複製

配列を明示的に複製して使いたい場合がある。 Go 言語では配列の複製はとても簡単である。

package main

import "fmt"

type Array4 [4]int8

func dump(ary *Array4) {
	fmt.Printf("ary(dump) = %p\n", ary)
	for i := 0; i < len(ary); i++ {
		fmt.Printf("%p: %v\n", &ary[i], ary[i])
	}
}

func main() {
	ary := Array4{0, 1, 2, 3}
	var ary2 Array4
	fmt.Printf("ary(org) = %p\n", &ary)
	dump(&ary)
	ary2 = ary
	dump(&ary2)
    if ary == ary2 {
		fmt.Println("ary == ary2")
	} else {
		fmt.Println("ary != ary2")
	}
}

実行結果は以下の通り。

ary(org) = 0x1040a124
ary(dump) = 0x1040a124
0x1040a124: 0
0x1040a125: 1
0x1040a126: 2
0x1040a127: 3
ary(dump) = 0x1040a128
0x1040a128: 0
0x1040a129: 1
0x1040a12a: 2
0x1040a12b: 3
ary == ary2

aryary2 が同じ内容の異なるインスタンスであることが分かると思う。 また配列同士の比較も同じ型であれば単純である。 たとえば [3]int8[4]int8 は異なる型と見なされるため単純比較はできない。

一方, slice の複製が欲しい場合は copy() 関数を使う。

package main

import (
	"fmt"
	"reflect"
)

type Array4 [4]int8

func dumpS(slc []int8) {
	fmt.Printf("pointer(dumpS) = %p\n", slc)
	fmt.Printf("size(dumpS) = %v\n", len(slc))
	fmt.Printf("capacity(dumpS) = %v\n", cap(slc))
	for i := 0; i < len(slc); i++ {
		fmt.Printf("%p: %v\n", &slc[i], slc[i])
	}
}

func main() {
	slc1 := []int8{0, 1, 2, 3}
	dumpS(slc1)
	slc2 := make([]int8, len(slc1), cap(slc1))
	copy(slc2, slc1)
	dumpS(slc2)
	if reflect.DeepEqual(slc1, slc2) {
		fmt.Println("slc1 == slc2")
	} else {
		fmt.Println("slc1 != slc2")
	}
}

コピー先の slc2 について make() 関数であらかじめサイズと容量を確保しておくのがポイント。 実行結果は以下の通り。

pointer(dumpS) = 0x1040a124
size(dumpS) = 4
capacity(dumpS) = 4
0x1040a124: 0
0x1040a125: 1
0x1040a126: 2
0x1040a127: 3
pointer(dumpS) = 0x1040a144
size(dumpS) = 4
capacity(dumpS) = 4
0x1040a144: 0
0x1040a145: 1
0x1040a146: 2
0x1040a147: 3
slc1 == slc2

slice 同士を比較するのも単純ではないが, reflect.DeepEqual() 関数が使える。

ブックマーク

参考図書

photo
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
Alan A.A. Donovan Brian W. Kernighan 柴田 芳樹
丸善出版 2016-06-20
評価

スターティングGo言語 (CodeZine BOOKS) Go言語によるWebアプリケーション開発 Kotlinスタートブック -新しいAndroidプログラミング Docker実戦活用ガイド グッド・マス ギークのための数・論理・計算機科学

著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。

reviewed by Spiegel on 2016-07-13 (powered by G-Tools)

辛いチョリソー
ハラミいえー
アスパラベーコン。チーズたっぷり
ホタテバターからスタート

March 11 2017

きみは Generics がとくいなフレンズなんだね,または「制約は構造を生む」 - remark

公理によって与えられる暗黙の制約。この制約が集合の要素同士をしっかり結びつける。単純にしばるのではない、相互に秩序ある関係を結ぶ。言い換えれば――公理によって与えられる制約が構造を生み出しているのだ
via 数学ガール/フェルマーの最終定理

今回は戯れ言モードなので「プログラミング言語 Go」ではなくこちらで書いてみる。 コードは1行も書かないのでご安心を(笑)

私は出自が組込みエンジニアで(今は何でも屋),アセンブラや C/C++ から始まり Java などの制御に向いていると言われる言語を遍歴している(PHP を機器制御に使うとかいうこともやったが)。 Go 言語もその流れから興味を持っているが,あいにく私が住んでいる地方都市で Go 言語の出番はまだない。

そういう経歴を持つ私から見て Go 言語が特異だと思ったのは以下の2点である。

  1. 例外処理がない
  2. 明示的なクラス定義構文がない

私だけでなく C++ や Java などから来た人は大抵これで面食らうらしい。

このうち1番目については「プログラミング言語 Go」で記事にしたので割愛する。

さて,2番目の「明示的なクラス定義構文がない」について。

そもそも「クラス」とはなにか。 クラスとは以下の要素をひとまとめの「モノ(object)」として定義したものである。

  • 名前
  • 属性
  • 操作

Go 言語では明示的なクラス定義構文がない代わりに typestruct,およびメソッド・レシーバを組み合わせることでクラスの要素である名前,属性,操作を定義できる。

そしてクラス定義で重要なのは「クラス間の関係」を定義することである。 クラス間の関係としては大雑把に以下の2つがある。

  1. 汎化・特化(継承 等)
  2. 関連(集約,依存 等)

このうち2番目の関連は定義しやすい。 あるクラスの属性として別のクラスを定義するか,操作によって関連付けるかすればいいからだ。 問題は1番目の汎化・特化をどうやって定義するかである。

Go 言語の場合は interface を使った duck typing 1 を採用した。 duck typing とはクラスの振る舞いに注目してクラス間の汎化・特化関係を帰納法的に定義することである。 例を挙げると,それが「にゃーん」と鳴くのなら机器猫だろうが猫耳メイドだろうがサーバルキャットだろうが全部「猫」である,ということだ。

クラス間の関係を定義するのは意外に大変である。 皆さんは「クラス設計」をどのように行っているだろうか。 まずは具体的なクラスを列挙していき,それらの関係を考察していくのではないだろうか(「ユーザ」や「管理者」を定義するのに 動物→人間→… と考えていく人はいないだろう)。 考察する過程で(クラスとクラスを繋ぐ)不可視のクラスを発見したり複数のクラスがひとつの概念で括れることに気づいたりすることもある。 つまり設計する過程では「具象→抽象」へと遡っていく。

一方,実装する際には, C++ や Java では最初にテンプレート・クラスやインタフェース・クラスを作ってからインプリメント・クラスに落とし込む。

たとえば,最初に「猫」という抽象クラスを作っておいて,それを継承する形で机器猫や猫耳メイドやサーバルキャットといった具体的なクラスを実装していく。 つまり「抽象→具象」へと作業していくわけだ。 そしてその過程において Generics2 は,ほとんど必須と言えるほど利用価値の高い機能と言える。

これが Go 言語による実装ではひっくり返る。 たとえば,最初に机器猫や猫耳メイドやサーバルキャットといった具体的なクラスを作っていって「これってみんな『にゃーん』って鳴くじゃん」と気がつけば後付けで「猫」という抽象クラスを実装できるのである。

どういうことかというと, Go 言語においては設計と実装を同時進行で「具象→抽象」へと考察していくことができる,ということである。 このような思考過程においては Generics の有無はさして重要ではなくなる。 だって具象化されたオブジェクトから作り始めるのだから。

「抽象→具象」へと実装する人にとっては Generics のない Go 言語はとてもまだるこしく見えるかもしれない。 「なんで Generics がねーんだよ。いちいち全部書かせる気か。このポンコツ言語が!」となること請け合いである。 しかし一度 duck typing に慣れた人にとっては抽象クラスから書かなければならない C++ や Java こそが面倒くさい。 何故なら,脳内では「具象→抽象」で思考していくのに実際に書くときには「考え終わらないと書けない3」からである。 Go 言語なら「考えながら書ける」のに。

これはどちらが正しいかという問題ではない。

たとえばウォータフォール型4 の開発スタイルでは実装を開始するまでに設計が終わることが(建前上は)保証されているため「抽象→具象」へと書き進めることが容易な言語が向いている。 一方,要件が絶えず変わったり実験的な製品の場合は設計が終わるまで待っていられないため Go 言語のような言語が向いてるかもしれない。 まぁ設計と実装を同時にやろうとするとリファクタリングが頻繁に発生するのでコピペ・プログラマにはキツい作業になるかもしれないが。

個人的には「プログラマは要件定義の段階から参加してコードを書くべき」と思ってるので,これを容易にするであろう Go 言語には注目している。

ブックマーク

参考図書

photo
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
Alan A.A. Donovan Brian W. Kernighan 柴田 芳樹
丸善出版 2016-06-20
評価

スターティングGo言語 (CodeZine BOOKS) Go言語によるWebアプリケーション開発 Kotlinスタートブック -新しいAndroidプログラミング Docker実戦活用ガイド グッド・マス ギークのための数・論理・計算機科学

著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。

reviewed by Spiegel on 2016-07-13 (powered by G-Tools)


  1. duck typing の由来は duck test だそうで, duck test とは “If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.” と帰納法的に対象を推測する手法を指すらしい。 duck typing のメリットのひとつは多重継承で発生する様々な問題(名前の衝突や菱形継承など)を気にする必要がない点である。 [return]
  2. 知らない人のために Generics について簡単に説明しておくと,変数の型あるいはインスタンス(instance)に対するクラス(class)に関係なく単一の記述で変数ないしインスタンスを扱うことのできる仕組みである。汎化の一種と考えてもよい。いわゆる多態性(polymorphism)とは異なり,継承関係の異なるクラスでも一緒くたに扱うことが可能なかなり強力な仕組みである。 Generics は特にコンテナ(container; オブジェクトの集まりを表現するデータ構造,配列など)操作で威力を発揮する。 [return]
  3. 私はこれを「写経」と呼んでいる。はっきり言ってプログラミングでもっとも苦痛なのがコーディング=写経だったりする。ちなみに一番好きなのはデバッグ。特に他人の書いたコードをデバッグするのは大好物。あれは極上の数理パズルである(締切さえなければね)。 [return]
  4. 「ウォータフォール型」とは滝の水が上から下へと落ちていくように 要件定義→設計→製造 と上流工程から下流工程へ順番にプロセスを進めていく開発スタイル。工程ごとにマイルストーンを設けてチェックを行い,各工程が完了しないと先に進めないようにする。まぁ実際にはスケジュールやらの関係でチェックを端折って先に進めてしまうことが多く,下流工程に入ってから致命的な欠陥に気づいて抜き差しならない状況に陥ることもしばしばある(笑) [return]

March 10 2017

オムポテ。中のポテトがうまうまー
Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.

Don't be the product, buy the product!

Schweinderl