こまぶろ

技術のこととか仕事のこととか。

EC2 Amazon Linux に Java(OpenJDK) / Tomcat / GitBucket / gitbucket-label-kanban-plugin をインストールする

思うところあり掲題の構成でサーバを立てたので、メモを残す。

  • EC2のAmazon Linuxインスタンスを作成し、
  • OpenJDKをインストールし、
  • Tomcat 8 を立て、
  • GitBucket を構築し、
  • gitbucket-label-kanban-plugin を導入

する手順。環境構築苦手マンなので、Unix系OSのコマンドの基本的な部分の補足も含む。間違っているところ、不要な手順があったらご指摘ください。

環境

  • macOS Mojave
  • バージョン 10.14.1

EC2インスタンスの作成

AWS コンソールからインスタンスを作成する。

f:id:ky_yk_d:20181129001638p:plain

AMI は、下のものを選択してみる*1

Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type - ami-063fa8762cdc9a5a6

AMIの選択以降は、デフォルトで進める。インスタンスを作成する直前に、キーペアを作成させられるため、pemファイルをダウンロードして適当なところに配置しておく(SSHアクセスする際にこのファイルへのパスを指定することになる)。

セキュリティグループの設定

セキュリティグループを新たに作成。インバウンドのルールを下記のように設定した。

タイプ プロトコル ポート範囲
HTTP TCP 80
SSH TCP 22
HTTPS TCP 443
カスタムTCPルール TCP 8080

※ソースは今回は自分のPCからのみのアクセスとしたいため「マイIP」で設定。

今回は、SSHmacのTerminalからのアクセス)、カスタムTCPルール(Tomcatのデフォルトポート)しか利用していないので、HTTPとHTTPSは要らないかもしれない。

Elastic IP の割り当て

発行して割り当てる。インターネット経由でアクセスしたいため。AWSコンソールからボタン数回で。

SSHでサーバにアクセス

sshコマンドでアクセスする。

webkaru.net

-i オプションは、秘密鍵ファイルを指定するもの。

$ ssh ec2-user@[Elastic IP] -i [秘密鍵のパス] 
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '[秘密鍵のパス]' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "[秘密鍵のパス]'": bad permissions
ec2-user@[Elastic IP]: Permission denied (publickey).

秘密鍵の権限が広すぎると怒られている。

yachibit.hatenablog.jp

たまにしかサーバを触らないので毎回忘れるパーミッションの表記をまとめておく。

数字 二進数 英字 意味
7 111 rwx 読・書・実行
6 110 rw- 読・書
5 101 r-x 読・実行
4 100 r--
3 011 -wx 書・実行
2 010 -w-
1 001 --x 実行
0 000 --- なし

上の表に従えば、0644はグループと他人に読み取りの権限がある。これがダメっぽいので600にする。あとで出てくる755と合わせてまとめておく。

数列 本人 グループ 他人
644 rw- r-- r--
600 rw- --- ---
755 rwx r-x r-x
$ chmod 600 [秘密鍵のパス]

再度SSH接続を試みる。

$ ssh ec2-user@[Elastic IP] -i [秘密鍵のパス] 

無事、アクセスできた。

f:id:ky_yk_d:20181128232649p:plain

Open JDK 1.8.0 のインストール

今回のインスタンスにはデフォルトで1.7.0系がインストールされている。

$ java -version
java version "1.7.0_191"
OpenJDK Runtime Environment (amzn-2.6.15.4.82.amzn1-x86_64 u191-b01)
OpenJDK 64-Bit Server VM (build 24.191-b01, mixed mode)

1.8.0が欲しいのでyumでインストールする。-y オプションは、すべての質問にyesと応答するもの。

$ sudo yum -y install java-1.8.0-openjdk-devel 

新たにインストールしたJavaを利用するように変更する。下記を参考に。

graziegrazie.hatenablog.com

$ sudo update-alternatives --config java

f:id:ky_yk_d:20181128233047p:plain

使用するJavaのバージョンが変更されていることを確認する。

$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

Tomcat のインストール

特に意味はないが、8.5.x の最新である 8.5.35 をインストールした。ちなみに最新版は 9.0.13 である。

Apache Tomcat® - Apache Tomcat 8 Software Downloads

下記の記事を参考にした。

qiita.com

ただし、Amazon Linux は sytemd ではなく init を使用することになるため、「初期設定 & 起動」以降の手順には従っていない。

qiita.com

$ cd /usr/local/src 
$ sudo mkdir tomcat
$ cd tomcat
$ sudo wget http://ftp.riken.jp/net/apache/tomcat/tomcat-8/v8.5.35/bin/apache-tomcat-8.5.35.tar.gz # 公式ミラーサイトから取得する
--2018-11-28 13:56:07--  http://ftp.riken.jp/net/apache/tomcat/tomcat-8/v8.5.35/bin/apache-tomcat-8.5.35.tar.gz
ftp.riken.jp (ftp.riken.jp) をDNSに問いあわせています... 134.160.38.1
ftp.riken.jp (ftp.riken.jp)|134.160.38.1|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 9642757 (9.2M) [application/x-gzip]
`apache-tomcat-8.5.35.tar.gz' に保存中

apache-tomcat-8.5.3 100%[===================>]   9.20M  27.8MB/s    in 0.3s    

2018-11-28 13:56:08 (27.8 MB/s) - `apache-tomcat-8.5.35.tar.gz' へ保存完了 [9642757/9642757]

www.atmarkit.co.jp

ダウンロードしてきたものを解凍する。

$ sudo tar zxvf apache-tomcat-8.5.35.tar.gz
$ ls
apache-tomcat-8.5.35  apache-tomcat-8.5.35.tar.gz

/opt/配下に移動し、tomcatユーザに権限を与える。

$ sudo mv apache-tomcat-8.5.35 /opt/
$ sudo useradd -s /sbin/nologin tomcat
$ sudo chown -R tomcat. /opt/apache-tomcat-8.5.35 
$ sudo ln -s /opt/apache-tomcat-8.5.35 /opt/tomcat # シンボリックリンクの作成

下記の内容をファイルに書き込む。

export CATALINA_HOME=/opt/tomcat

$ cd /etc/profile.d
$ sudo vi tomcat.sh

tomcat を立ち上げてみる。

$ sudo -u tomcat /opt/tomcat/bin/startup.sh
Using CATALINA_BASE:   /opt/tomcat
Using CATALINA_HOME:   /opt/tomcat
Using CATALINA_TMPDIR: /opt/tomcat/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /opt/tomcat/bin/bootstrap.jar:/opt/tomcat/bin/tomcat-juli.jar
Tomcat started.

http://[Elastic IP]:8080 にアクセスする。

f:id:ky_yk_d:20181128234015p:plain

アクセスできるようになった。

Gitbucket のインストール

下記をインストールしたい。

github.com

下記を参考にする。

qiita.com

インストールする。

$ cd /opt/tomcat
$ sudo chmod webapps 755 # tomcat ユーザーにしかRead権限がない状態(drwxr-xr-x)ので、権限を追加
$ sudo wget https://github.com/takezoe/gitbucket/releases/download/4.29.0/gitbucket.war -O gitbucket.war
$ sudo -u tomcat /opt/tomcat/bin/startup.sh # tomcat を再起動する

http://[Elastic IP]:8080/gitbucket/ にアクセスする。

f:id:ky_yk_d:20181129000014p:plain

アクセスできた。

Kanban プラグインのインストール

下記をインストールする。

github.com

GitHubのREADMEには下記のようにある。

Download jar file from the release page and put it into GITBUCKET_HOME/plugins.

GITBUCKET_HOMEは、GitBucketを動かしているユーザのホームにある.gitbucketディレクトリらしい。移動する。

$ cd /home
$ sudo chmod 755 tomcat
$ cd tomcat
$ sudo chmod 755 .gitbucket
$ cd .gitbucket
$ sudo chmod 755 plugins 
$ cd plugins # ここが `GITBUCKET_HOME`/plugins
$ sudo wget https://github.com/kasancode/gitbucket-label-kanban-plugin/releases/download/2.0.3/gitbucket-label-kanban-plugin_2.12-2.0.3.jar 
$ sudo -u tomcat /opt/tomcat/bin/startup.sh # tomcat を再起動

root/rootでログインして、リポジトリを作ると、サイドバーに「Kanban」が現れている。クリックするとアクセスできる。

f:id:ky_yk_d:20181129001218p:plain

Kanbanをためす

Label に TODO と DOING を作成し、サンプルの Issue を作って動かしてみた。

f:id:ky_yk_d:20181129012307g:plain

ぬるぬる動く。

ちなみに、Gif画像の作成は下記の記事を参考にした。便利な世の中。

qiita.com

感想

取り組み始めてみればそこまで難しくはなかった。新しいツールを自力で導入できることは、プロセス改善のためには必須スキルなので、少しずつ磨いていきたい。

また、Trello などのクラウドのツールを使えない現場で働くみなさんは、GitBucket + gitbucket-label-kanban-plugin でカンバン生活を始めてみてはいかがだろうか。。

カンバン仕事術

カンバン仕事術

*1:Javaが入っているというのに惹かれての選択だが、入っているのが1.7.0系だったため結局入れ直すことになった。

開発組織のプラクティスや文化を科学する:『LeanとDevOpsの科学』書評

11月22日(昨日!)に発売されたばかりの、『LeanとDevOpsの科学[Accelerate] テクノロジーの戦略的活用が組織変革を加速する』を読んだ。

この本は、すぐに現場に活かせる新しい知見を提供するものことを主眼としたものではない。タイトルにあるように、科学の本なのだ。しかし、人文系出身の自分にも読みやすい形で、論文の成果を提示してくれる。そして、その成果の中には、現場のエンジニアとして役立てられるものも含まれている、価値のある書籍だ。以下、紹介したい。

書籍の特質

科学的アプローチ

ソフトウェア分野においては、プラクティスを紹介するような書籍が数多く出版されている。それらは、読者が自らの現場で闘うための武器を与えてくれる。

本書は、そういった(優れたものを数多く含む)書籍とは、趣きを異にしている。この書籍と一般的な書籍との差異は、この書籍が科学的なアプローチを採用している点にある。「はじめに」には以下のようにある。

本調査研究は、現在まだ市場で満たされていないニーズに応じるためのものである。目標は、「慣例上、学問の世界でしか用いられてこなかった厳格な研究方法を用い、結果を産業界にも公表することで、ソフトウェアの開発とデリバリの状況を改善すること」である。単なる逸話やチームの体験談を提供するのではなく、統計的に有意な方法でパフォーマンスの改善を促すケイパビリティを特定・理解する方法を確立することで、業界全体の水準向上の一助となるのではないかと考えた。*1

この書籍も、様々な武器を読者に提供してくれる。しかし、「その武器がどのような事柄に対してどのように有用か」を科学的手法によって明確に示している点がこの書籍の特徴だ。書籍の中の語を用いて説明を試みるならば、計測したい複数の「構成概念」を設定し、これを計測するためのアンケート調査を実施し、統計学の手法を用いて、これらの構成概念間にどのような相関や因果関係があるのか(ないのか)を明らかにしているのだ。

主題

本書で計測対象としている構成概念には、様々なものが含まれる。 一部を紹介すると、「ソフトウェアデリバリのパフォーマンス」、「帰属意識の強化」、「バーンアウトの軽減」、「デプロイ関連の負荷」、「組織全体のパフォーマンス」、「Wesrtumが推奨する組織文化」などがある。「組織文化」のような一見すると科学的手法になじまないようなものも扱っている点が面白い。

本書の主要なテーマである「デリバリのパフォーマンス」について少しだけ紹介する。デリバリのパフォーマンスを測定する尺度として、本書では以下の4つを採用している。

  • 変更のリードタイム
  • デプロイの頻度(年間デプロイ件数)
  • 平均修復時間
  • 変更失敗率

これらの尺度によって測定されるデリバリのパフォーマンスで、チームはハイパフォーマー、ミドルパフォーマー、ローパフォーマーの3つに区分される(クラスター分析)。この区分を用いて、どのような他の構成概念に「デリバリのパフォーマンス」が相関・因果関係を持つのかを明らかにするのが本書のメインテーマである。

各部の簡単な紹介

第1部

第1部「調査結果から見えてきたもの」では、デリバリのパフォーマンスを中心とした、様々な構成概念の間に見出される関係が説明される。詳述は控えるが、そこでは「継続的デリバリの実践度が増すと、職務満足度ややりがいが高まる」*2や、「WIP制限が単独ではデリバリのパフォーマンスの有力な予測尺度になりえない」*3などといった興味深い命題も提示される。

第2部

第2部「調査・分析手法」では、第1部の成果を導き出した研究の手法が説明される。研究はアンケート調査に基づいているのだが、このアンケート調査に対する不信に対しても丁寧に応えている。この第2部の記述は、統計学やアンケート調査の学術的トレーニングを受けていない人に向けて書かれており、どのような質問項目が適切か、質問項目によって「構成概念」が適切に計測できているかをどのように判断するのか、なども解説される。この第2部は、他のソフトウェア分野の書籍にはない貴重な部分だろう。

第3部

第3部では、第1部で紹介された知見を実際に応用する助けとして、ING Netherlandsにおける組織改善の現場事例が紹介されるとともに、そこで観察されたプラクティスが整理した形で提示される。類書の事例・プラクティス紹介と一線を画しているのは、プラクティスのなかで本書の研究でパフォーマンスの高さと相関があることが示されたものに印が付けられていることだ。末尾近くで、高パフォーマンス文化を獲得するためには、「エビデンスに基づく実験や学びを繰り返し、各組織の状況や組織文化にふさわしい新たな協働方式を開発していかなければならない」*4と述べられている点にも、この書籍全体を貫く精神が反映されていると言えるだろう。

雑感

冒頭でも述べたように、本書で紹介されている知見は、必ずしも目新しいものではない。現場と向き合うための新しい知恵を求めて本書に臨む人は、物足りなさを感じることさえあるだろう。

しかし、それは本書の価値を減ずるものではない。本書の価値は、科学的アプローチで我々の「事実だと思っていたこと」を裏付けている点にある。プラクティスの有効性に根拠を求めるような人(あるいは、そのような人にプラクティスの導入を説得しようとする人)には、本書は実に有用であるだろう。折しも下記のようなTweetをしていた自分には良い模範となる書籍だった*5

最後に、本書でソフトウェアのデリバリのパフォーマンスを改善するのに有用だと指摘されるケイパビリティのリストを示しておく。下記の内容に関心を持たれた方は、本書を手に取られてはいかがだろうか。


  • 継続的デリバリの促進効果が高いケイパビリティ
    • バージョン管理
    • デプロイの自動化
    • 継続的インテグレーション
    • トランクベースの開発
    • テストの自動化
    • テストデータの管理
    • 情報セキュリティのシフトレフト
    • 継続的デリバリ
  • アーキテクチャ関連のケイパビリティ
    • 疎結合のアーキテクチャ
    • チームへの権限の付与
  • 製品・プロセス関連のケイパビリティ
    • 顧客フィードバック
    • 業務プロセスの可視化
    • 作業の細分化
    • チームによる実験
  • リーン思考に即した管理・監視に関わるケイパビリティ
    • 変更承認プロセス
    • 監視
    • プロアクティブな通知
    • 進行中の作業(WIP: Work in Progress)の制限
    • 作業の可視化
  • 組織文化に関わるケイパビリティ
    • Westrum推奨の創造的な組織文化
    • 学びの支援
    • チーム間の協働
    • 職務満足度
    • 改善を推進するリーダーシップ

*1:xxiiページ。

*2:60ページ。

*3:93ページ。

*4:227ページ。

*5:Tweet中の「ソフトウェア工学!」は、昨日開催された「レガシー感謝の日」という勉強会における福々亭ひろにゃんこ (@warumonogakari)さんの発表にあった研究への言及である。イベントの内容および福々亭ひろにゃんこさんのスライド資料についてはkojirockさんのレポート記事を参照していただきたい。

アジャイルとUnlearn(まなびほぐし、脱学習、学習棄却)についての覚書:野中郁次郎、鶴見俊輔、あるいはヨーダ

"Unlearn"という言葉は、どれだけの人にとって馴染みのあるものだろうか。

僕はこの言葉に、学生時代に出会っている。教育学系の大学院にいたので、自分自身の研究テーマとは離れているものの、「学び」とは何かというような議論にも接することがあった。その中で、「Unlearn=学びほぐし」という言葉遣いがなされていたことを記憶していた。

今年になってから、この言葉に複数回接することがあり、少しこのUnlearnについて調べてみた。この記事では、この辺りの経緯と途中経過としてまとめておく。問題意識に導かれることも、学術的に網羅性を考慮して調査をしたわけではないので、あくまで覚書である。

日本のアジャイルの文脈でのUnlearnとの再会

この言葉に、直近では、11月12日にKDDI DIGITAL GATE で開催された下記のイベント*1での及部(@TAKAKING22)さんの発表でこれに接した。

techplay.jp

今回の及部さんの発表の中でUnlearnが占めている地位は必ずしも大きくなく、及部さんがUnlearnをどのように捉えているのかは明らかではなかった。この点については、Regional Scrum Gathering Tokyo 2019 での公募セッションで及部さんご本人がUnlearnについて語るようなので、期待したい。

confengine.com

ソフトウェア業界の勉強会でUnlearnに出会うのはこの時が初めてではなかった。8月23日にデンソーで開催されたイベント*2における「やっとむ」こと安井力(@yattom)さんの発表でもUnlearnが「脱学習」として紹介されていた。

connpass.com

やっとむさんがUnlearn=脱学習について述べたのはこれが初めてではない。少し調べただけでも、下記の2017年12月のスライド資料に同じ表現がある。いつ頃から使うようになったのかはわからないが、翻訳をたくさんされているやっとむさんなので、英語の日常語として知っていたのかもしれない。この辺りはご本人に伺ってみたい。

なお、このイベントでは、後半に登壇者3名と聴衆の希望者との討論セッションがあったのだが、そこでもUnlearnについての言及があった。「形あるアジャイルの先に何があるのか?」という話題になった時に、きょん(@kyon_mm)さんが、「Unlearnの仕方がまとまることが次のプラクティスになるのではないか」と仰っていた。ここでのUnlearnは、「いま」に対して最適なものを素早く構築するためには、それまでの仕事に対して最適化された状態を抜け出す必要があるという話だったと思う。

ky-yk-d.hatenablog.com

上記のこともあり、11月12日のイベントの懇親会で、ちょうどやっとむさんを交えてお話しする機会があったので、「Unlearnってアジャイル界隈で流行りなんですかね?」と伺ってみたところ、確かによく見るというような趣旨の返事があった(と記憶している)。また、同席していた横道(@ykmc09)さんからAgile 2018でも言及されていたと教えていただいた。

そこで、調べてみたところ、アトラシアン社のDominic Price(@domprice)氏による初日基調講演のスライドに、Unlearnという表現が出てきていた。

"agile unlearn"でGoogle検索をすると、非常に多くの検索結果(引用符付き検索で60,000件)が得られる。各国のアジャイルのカンファレンスのスライド資料も複数ヒットした。後述のように、Unlearnというのは専門用語ではない通常の英単語なので、この数が出てくることは不思議ではないのだが、Unlearnという言葉がアジャイルに無縁な言葉でないことは確かだろう。

野中郁次郎と竹内弘高におけるUnlearn

Unlearnという言葉を、別の、しかしアジャイルにとってはとても身近な文脈にも見出すことができた。それは、「スクラムの父」である野中郁次郎の文献だ。

野中郁次郎と竹内弘高の『知識創造企業』の索引にもUnlearn(ing)が「学習棄却」という訳語とともに現れている。以下に、言及のある箇所を示す。

組織進化論の発見の一つに、「適応は適応能力を締め出す(Adaptation precludes adaptability.)」というのがある。過去の成功への過剰適応(overadaptation)だといってもよい。恐龍がその一例である。太古の一時期、この生き物は、生理的にも形態的にもある一定の環境に適合していた。しかし、その環境に適応しすぎて、ついには気候とエサとなるものの変化についていくことができなかった。日本軍は同じ罠に陥ったのである。過去の成功に過剰適応して、変わりつつある新しい環境の中でそれらの成功要因を「学習棄却(unlearn)」することに失敗したのである。(『知識創造企業』248頁)

この箇所では、日本軍の失敗がUnlearnの失敗であったと指摘されている。また、他の箇所では、『学習する組織』のピーター・センゲの組織学習の理論が、"「ダブル・ループ学習」すなわち「学習棄却(アンラーニング)」(Hedberg, 1981)というコンセプトと組織開発(Organizational Development)への強い志向に関係"*3する欠点によって批判されている*4

この書籍には、他にも目を引くものがある。それは、参考文献一覧に掲げられている、"Imai, K. , I. Nonaka , and H. Takeuchi. 1985 "Managing the New Product Development Process: How Japanese Companies Learn and Unlearn と題する論文だ。興味深いのは、この論文を引用している116頁で、この論文を用いて下記の図を説明していることだ。

この図の出典は"Takeuchi and Nonaka (1986)"、すなわちスクラムの原典とされる"The New New Product Development Games"だ。時系列的には、上述の論文はこのスクラムの原典の前の年に書かれているわけだが、内容上の繋がりがあるようだ。野中郁次郎の著作はじっくり読んだことがないので、これから掘り下げてみたいと思う。

blogs.itmedia.co.jp

鶴見俊輔の「まなびほぐし」

Unlearnという言葉は、大学院時代の僕の近辺では「学びほぐし」と訳されていた。いい訳ですよね、という話を懇親会ではしていたのだが、調べてみたところこの訳を提案したのは哲学者の鶴見俊輔であるというのが有力である。

鶴見がUnlearnという言葉を初めて知ったのはハーヴァード大の学生だった時代にヘレン・ケラーと会ったときで、その時に"型どおりにセーターをあみ、ほどいて元の毛糸にもどして自分の体に合わせて編みなおすという情景が想像され"*5、"日本語になおせば「学びほぐす」ということになる"*6と語っている。

この「学びほぐし」を主題にしているシリーズ『ワークショップと学び』(東京大学出版会)が刊行されている。今回、その第一巻(全三巻)である『まなびを学ぶ』を手に取ることができた。第1章で、佐伯胖は以下のように指摘している。

「まなびほぐし(アンラーン unlearn)」というのは、「まなび(learn)」のやり直しである。しかし、「やり直し」と言っても、これまで学んできた知識や技能を「帳消しにする」などということができるわけはない。「一たす一は二である」という知識を、あえて「なかったことにする」わけにはいかない。ここはやはり、これまでの「まなび」。通して身に付けてしまっている「型」としての「まなびの身体技法(まなび方)」について、それをあらためて問い直し、「解体」して、組み替えるということを意味しているのであろう。(『まなびを学ぶ』62頁)

「解体」の動機は、高木光太郎の下記の論述にも存在する。「学びほぐし」とは、学習したことを単に忘れるのでも、新たな知識を学習するのでもない。長くなるが引用する。

 まずまなびをゴールとの関係からではなく、不確定の未来に向かう変化のプロセスとして捉える必要がある。「まなびほぐし」は「自分の体に合わせたセーター」を手に入れることを目的としているわけではない。そうであるなら新品の毛糸を用意し、自分の体型にぴったりの型紙を使ってセーターを編み上げたほうが効率的である。ゴールを明確に意識し、まっさらな状態からスタートして自分に必要な知識や技術を段取りよく獲得していく。オーソドックスな学習論である。これに対して「まなびほぐし」の学習論は、一旦編み上げられた知が解体されつつ、不安定に揺らぎながら何か新しいものへと変化していく過程そのものに焦点をあわせる。揺らぐ現在から、少しずつ未来の姿が浮かび上がってくるプロセスをまなびとして捉えようとするのである。どこかで誰かによってあらかじめ定められた未来に向かうのではなく、まだ姿がよく見えない未来の時間を「いまここ」で生成する。これが「まなびなおし」の学習論におけるまなびの基本的な姿である。
 それゆえ「まなびほぐし」の学習論は、まなびの現場で生まれる「混乱」「戸惑い」「躊躇」「食い違い」「対立」といった「揺らぎ」に肯定的な可能性を見出そうとする。「まなびほぐし」のプロセスにおいて、既存の知が解体されて生じる不安定状態はまなびの阻害要因ではない。それはむしろ未来の時間が創造される生成の場である。(『まなびを学ぶ』24頁。強調は引用者。)

ここで語られている、「揺らぎ」への肯定的な評価は、先に紹介した野中・竹内の『知識創造企業』で「知識創造を促進する第三の組織的要件」として指摘されている「組織と外部環境との相互作用を刺激するゆらぎ(fluctuation)と創造的なカオス(creative chaos)」と文言上は符合する*7。現時点で強い主張をするつもりはないが、掘ってみても面白いであろうと感じたので書き留めておきたい。

また、古めの文献として、川本隆史「才能のプーリング・自己所有権・ケイパビリティ-「基礎学力」概念の《編み直し》のために 」(2005)を見つけることができた。ここでは、ウォーラーステインが著書のタイトルに用いた"Unthinking"と並べてUnlearnに言及されている。《編み直し》というのは川本がウォーラーステインの"Unthinking"を解釈した語で、以下のように説明が加えられている。

Unthinkという動詞は、シェイクスピアにも用例のある由緒あるものだそうで、unは「否定」ではなくて「戻す」とか「振りほどく」という反復行為を表す接頭語です。

このUnthinkを巡っては、川本が引用しているように鶴見俊輔の論文「Unthinkをめぐって -日米比較精神史」(『リベラリズムの苦悶-イマニュエル・ウォーラーステイン が語る混沌の未来』所収)がある。出版年が1994年と古いが、川本の記述によればこの論文でも鶴見はUnlearnに言及しているようである。この本も図書館で利用できることがわかったので、近日中に目を通してみたいと思う。

一般的な英単語としてのUnlearn

調べてみてわかったのは、Unlearnという言葉自体は、西洋においては普通に用いられる言葉であるらしいということだ。Web上の英語の辞書でも、複数のものにそのままズバリの単語が収録されている。

また、スターウォーズの『帝国の逆襲』におけるヨーダのセリフでも用いられている。沼の中の戦闘機をフォースで持ち上げようとするルークが、戦闘機は小石と違って大きすぎると言い訳をする。それに対してヨーダが言うのが下記のセリフだ。

No! No different! Only different in your mind. You must unlearn what you have learned.

このセリフについては、このまとめに記載されていた以下のTweetで知った。このシーンを見たことはあったが、特に印象に残っていなかった(英語リスニング力・・・)。

終わりに

以上、極めて雑多ではあるものの、数日でUnlearnについて調べたことを書いておいた。文献を読み込むには至っておらず、文言上の符合を見つけて喜んでいるに過ぎないが、アカデミズムの世界に生きている訳ではないので、とりあえず公開しておく。時間がある時にまた掘ってみたいと思う。

【以下、公開後の追記】

公開後に、KIDANI Akito(@kdnakt) さんから、以前「アンラーニング」について書いたという記事を教えていただいた。

kdnakt.hatenablog.com

上の記事で、中原淳が「学習棄却」という言葉を用いている。どうやら、ヘドバーグ(『知識創造企業』に出てきているHedberg)の「アンラーニング理論」というものがあって、そちらの分野では「学習棄却」が定訳になっているらしい。ビジネスパーソン向けのWebサイトにも以下のように紹介されている。

アンラーニングを常にしていくしかない。アンラーニングは組織学習の研究者であるヘドバーグにより提唱されたものだ。

corporate-learning.jp

つまり、以下のようなことが言えるのではないか。Unlearnには、(ダブルループ学習のアージリス=ショーンもその代表者であるところの)組織学習の分野でヘドバーグが主張した「アンラーニング理論」の文脈で用いられる「学習棄却」と、ヘレン・ケラーの日常語から鶴見俊輔が持ち帰り、教育学の分野で用いられる「学びほぐし」の少なくとも二つの系統があるということだ。

掘れば掘るほど面白そうだ。

参考文献

*1:MezRyuKa(@MzRyuKa)さんによる イベントレポートがある。

*2:Qakinkさんによるイベントレポートがある。

*3:『知識創造企業』66頁。

*4:「ダブル・ループ学習すなわちアンラーニング」も、追いかけがいがある。ダブル・ループ学習を提唱したのはアージリスとショーンであり、ショーンはデューイの研究者である。そして鶴見俊輔もまた、デューイの研究で知られている。

*5:鶴見俊輔「対談の後考えたーー臨床で末期医療見つめ直す」『朝日新聞』2006年12月27日付朝刊。苅宿俊文・佐伯胖・高木光太郎編『まなびを学ぶ』62頁より孫引き。

*6:『KAWADE 道の手帖 鶴見俊輔 いつも新しい思想家』20頁、中井久夫との対談での発言より。

*7:『知識創造企業』116頁。

TypeScriptとJavaとの違いによるつまずきと「循環参照」

『テスト駆動開発』をTypeScriptで写経していて、Javaとの違いで大きくつまずいた。

結論からいえば、モジュールの循環参照で空オブジェクトが生まれていた、というシンプルな原因だったのだが、明らかにできるまでにはかなり時間がかかった。鮮やかとは程遠い手際の調査をしてしまったので、どのように当たりをつけて、どのように調べていったか、その過程で知ったこと、などを書いておく。

なお、TypeScriptの環境は前回の記事で構築したものだ。

ky-yk-d.hatenablog.com

TypeScriptはJavaと構文が近しい言語だから、Javaのサンプルコードを見ながら写経をするのはハードルが高くないし、それでいて細かい部分でのJavaとTypeScriptとの違いを把握することができると考えたからだ。

TypeScriptのユニットテストをmocha + power-assert で書く - こまどブログ

これは完全にフラグだった*1

つまずき

スーパークラスでサブクラスのコンストラクタを呼び出す

つまずくことになったのは、第8章「実装を隠す」で、サブクラス(DollarとFranc)を消してしまいたいという意図から、MoneyクラスにDollarクラスのインスタンスを返すstaticメソッドを追加するところだ。

public class Money {
    protected int amount;
    public boolean equals(Object object) {
        Money money = (Money) object;
        return amount == money.amount && this.getClass().equals(money.getClass());
    }
    static Dollar dollar(int amount) {
        return new Dollar(amount);
    }
}

staticメソッドでインスタンスを返すというのはよくある。特に疑問を持つことなく、以下のようにTypeScriptで記述して、テストを実行したところ、エラーになってしまった。

import { Dollar } from "./dollar";

export class Money {
  constructor(protected amount: number){}

  static dollar(amount: number):Dollar{
    return new Dollar(amount);
  }
}

f:id:ky_yk_d:20181110235145p:plain

当初の推測

TypeError: Object prototype may only be an Object or null: undefined。この時点で、「あークラスの循環参照が悪いのかなー」と予想した。JavaとTypeScript(JavaScript)のクラスは構文は似ていても、クラスベースのオブジェクト指向言語とプロトタイプペースのオブジェクト指向言語で実装が全然違う、というのは知っていたので、Javaでは許されることがTypeScript(JavaScript)で許されなくても不思議はない。とはいえ、あっさり「じゃあ循環依存やめればいいんでしょ」としてしまうと学びがないので、地道に追っかけてみることにした。

謎解き

JavaScriptにコンパイルしてコードを追う

at setPrototypeOf (<anonymous>)と言われても、そんな関数はTypeScriptのコードの中では使っていない。そこで、JavaScriptにコンパイルしたものにテストを実行してみることにした。tscコマンドで出力されたjsファイルをコピーして、pure JavaScript用のテスト環境でテストを実行した。tsconfig.jsonで設定するバージョンはes5にした。

f:id:ky_yk_d:20181111000948p:plain

dollar.jsのextendStatics(d, b)というところでエラーになっているらしい。以下に問題のdollar.jsを示す。JavaScript初心者にも読めなくはないコードだ。

"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);  // ←どうやらこれらしい(_extendsが呼び出されると実行される)
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var money_1 = require("./money");
var Dollar = /** @class */ (function (_super) {
    __extends(Dollar, _super);  // 上記の関数が呼び出されるのはここ
    function Dollar(amount) {
        return _super.call(this, amount) || this;
    }
    Dollar.prototype.times = function (multiplier) {
        return new Dollar(this.amount * multiplier);
    };
    return Dollar;
}(money_1.Money));
exports.Dollar = Dollar;
//# sourceMappingURL=dollar.js.map

console.logで調べてみたところ、extendStaticsには、setPrototypeOfが代入されていた。論理演算子が使われており、前から順番に評価して、定義されているものがあれば代入するというコードになっている。ここでは、最初のObject.setPrototypeOfが代入されていることから、ES2015の環境で実行されているということだろう(getPrototypeOfはES5から、setPrototypeOfはES2015から)。

問題の特定

ここで、Object.setPrototypeOfの仕様を見てみると、第二引数のprototypeがオブジェクトあるいはnullである必要があると書かれている。再びconsole.logで追いかけてみると、__extends(Dollar, _super)の手前で_superが未定義となっている。どうやらこれがエラーの直接の原因だ。

developer.mozilla.org

では、なぜ_superは未定義となっているのか。_superはあくまで仮引数だから、実引数として渡されているのが何かを考えなければいけない。答えはすぐ近くにある。関数リテラルの{}の直後、()で渡されているmoney_1.Moneyが実引数だ。そしてmoney_1は、以下の部分で定義されている。

var money_1 = require("./money");

このrequireはCommonJSの仕様らしい。自分の場合はNode.js上でmochaを動かしているので、Node.jsのrequireの実装ということになる(はず)。存在しているファイルを指定しているのに未定義となるというのは、どういうことなのだろうか。バッチリ疑問に答えてくれる記事があった。

qiita.com

CommonJSモジュールのrequireの仕組み

  • CommonJSモジュールでは、モジュールがrequireされた際に、module.exportsというオブジェクトが空オブジェクトで初期化される。
  • module.exportsに値を加えたり、オブジェクトを置き換えたりすることでそのモジュールをrequireした戻り値としてmodule.exportsの中身を使えるようになる。
  • さらに、CommonJSでは同じファイルを二度requireしようとした場合、二度目はスキップされてその時点で定義されているmodule.exportsの内容を戻り値とする。
  • これら2つの仕組みが重なり、循環参照が発生した際、多くの場合では片方のモジュールは空オブジェクトになってしまう

今回、先にテストコードからはmoneyモジュールをrequireしていて、その中でdollarモジュールをrequireしている。そして、dollarモジュールの中で、moneyモジュールを再度requireしているのが上の箇所だ。確認のため、requireの直後にconsole.log(money_1)を実行するようにしてみると、{}と表示された。まさに上記の記事にあるように、空オブジェクトがmoney_1に代入されている。undefinedと言われていたのは、money_1にはMoneyなどというプロパティが存在しないからだということがわかった。

その他諸

クラスの循環参照自体に問題はない

ここまできて、自分の最初の推測「クラスの循環参照が原因だ」が誤りだったということがわかった。問題なのは、クラスの循環参照ではなく、モジュール同士の循環参照だ。では、モジュールの循環参照を無くせば正常に動作するのだろうか。

これを試すのは簡単だ。DollarクラスをMoneyクラスと同じファイルに移動してやればいい。久しぶりにTypeScriptの世界に戻ってコードを書き換えたところ、エラーは発生せず、テストも通すことができた。クラスの循環参照自体は、Javaと同様に問題を産まないようだ*2

活かしきれなかった先人の知恵

実は、この答えにはもっと早くたどり着けるはずだった。JavaScriptにコンパイルして実行してみるということをしている最中に、JavaScriptで写経をしているGitHubのリポジトリを見つけていた。そのコミットログから第8章以降のコードをみたところmoney.jsという1つのファイルにMoney, Dollar, Francクラスが全て収められていた。ES2015のclass構文で記述されていたので、そちらが何か影響を及ぼしているのかと考えてスルーしていたのだが、第7章が終わったあとから第8章が終わったところの差分は以下のようになっていた。

github.com

この方も、当初はMoneyとDollarを別ファイルで作成していたのに、このコミットで1つのファイル(money.js)にまとめていたのだ。これを早くに見ていれば、問題がクラスの循環参照にはないことはわかったはずだった。迂闊だったとしか言いようがない。

Java、C++におけるクラスの循環参照

Javaの場合は、別々のファイルに書かれているクラス同士が循環参照になってもエラーにはならないことは事実としてはわかっている。せっかくなので、クラスの循環参照について調べてみた。

https://ameblo.jp/blueskyame/entry-10372584472.htmlameblo.jp

stackoverflow.com

はっきりとしたことはわからなかったが、C++とJavaで挙動が違うこと、メモリの使い方が関わっているらしいことがわかった。コンパイラが何をしているかとも関係しているようだ。踏み込むとまた面白いのだろうけれど、一旦退却することにした。

おわりに

上の記事などをみていて、思い出したことがある。新人研修時代、「オブジェクト指向面白そうだな」と手にとった『オブジェクト指向でなぜ作るのか』で、ヒープ領域とスタック領域、静的領域が・・・という説明がされていた。それまでstatic変数/メソッドが何なのかをよく理解できていなかったのが、その説明でだいぶ腑に落ちた気がしたのだった。

新しい言語やツールに触れる機会があっても、あまり「どうしてそうなっているのか」を考えなくなっていたかもしれない。今回のようにゆっくり時間をとってつまずいた箇所を掘ってみるといいことがあるなぁと思う一方で、日頃の知的怠惰を痛感させられた。

*1:意図通り、ともいう。

*2:ただし、同一ファイル内に複数のクラスを書かないといけなくなるような循環参照は、そもそも設計が悪いような気もする。とはいえ、今回のコードはリファクタリングの途中で一時的に発生する状態だったに過ぎない。

TypeScriptのユニットテストをmocha + power-assert で書く

故あってAngularを勉強しはじめた。

JSフレームワークとしては、Vue.jsを以前勉強していたので、「Angularではこうなのか」とか「これはVueにはなかったな」とか比較しながらドキュメントを読んで簡単な実装を進めている。

TypeScriptとJSフレームワーク

Angularの勉強をはじめてみて、当然出会うのがTypeScriptだ。

www.typescriptlang.org

Angularでは、TypeScriptが「開発の主要言語」とされており、公式のチュートリアルもTypeScript前提で進められる。三大フレームワークの中でも大規模開発との評判を聴くAngularにおいては、静的型付け言語であり、TS公式が"JavaScript that scales(スケールするJavaScript)"と自ら称するところのTypeScriptを採用するのが自然ということだろう。

TypeScriptは、Angularアプリケーション開発の主要言語です。

Vue.jsでも、TypeScriptを利用することは可能だ。ただし、あくまでもTypeScriptをサポートするという状態であって、標準的な開発言語はJavaScriptである。僕がVue.jsを勉強していたときも、JavaScriptで書いていた。

jp.vuejs.org

TypeScriptで『テスト駆動開発』の写経をする

TypeScriptに慣れ親しむために、『テスト駆動開発』の第1部の写経をしてみることにした。TypeScriptはJavaと構文が近しい言語だから、Javaのサンプルコードを見ながら写経をするのはハードルが高くないし、それでいて細かい部分でのJavaとTypeScriptとの違いを把握することができると考えたからだ。

https://github.com/ky-yk-d/ts-practicegithub.com

mocha + power-assert の環境を整える

『テスト駆動開発』の写経をするためには、ユニットテストの環境を整えなければならない。Angular CLIでは、プロジェクトやコンポーネントを作成したときに自動でテスト用のコードも用意されるようになっていて、そこで採用されているテスティングフレームワークはJasmineだ。

Jasmineでそのままやっても良かったのかもしれないが、それはAngularを使っていけば勝手に使うようになるだろうという考えもあって、以前記事にしたmocha + power-assert でのユニットテスト環境をTypeScriptでも整えてみることにした。

ky-yk-d.hatenablog.com

ちなみに、一度試してみて面白かったのは、下記の記事で紹介されているやり方だ。この記事は、とても丁寧に書かれていて勉強になった。カバレッジを自動で計測するというのはやったことがなかったので、これからまた取り組んでみたいと思う。

http://blog.catalyst-system.jp/useful-001/blog.catalyst-system.jp

espower-typescript を導入する

power-assertのReadMeをみてみると、

supports TypeScript.

とある。リンク先のページは、espower-typescript。ドキュメントの記載と実際の使い方を見た限りでは、TypeScriptに対応するだけでなく、上記の記事で利用していた intelli-espower-loader と同様の役割を果たしているようだ。intelli-espower-loader についてはこの記事に少し突っ込んだ記載があった。

espower-typescriptの導入方法は、ReadMeに書かれている。プロジェクトのルートで下記のコマンドを実行する。

npm install -D espower-typescript power-assert mocha typescript @types/node @types/mocha

ここで、-Dというオプションが登場している。「これはどういう意味なんだろう」と調べたところ、以前の記事で使っていた--save-devの省略表記であるとのこと。-dというオプションも別に存在しているが、全く意味が違う。実にややこしい。

https://docs.npmjs.com/cli/installdocs.npmjs.com

型定義ファイルについて

上のコマンドでは、以下の6つのパッケージをインストールしている。

  • espower-typescript
  • power-assert
  • mocha
  • typescript
  • @types/node
  • @types/mocha

馴染みのない@のついた下2つは、型定義ファイルを管理するものらしい。型定義ファイルというのは、Type ScriptのファイルからJavaScriptのライブラリを用いる際に必要になるもの。『速習TypeScript』から説明を引用しよう。

TypeScriptで本格的なアプリを開発するようになると、 JavaScript製のライブラリを活用したい、という状況は、ごく自然に発生します。もっとも、 JavaScriptライブラリには、 TypeScriptが要求する型情報が存在しないため、そのままでは正しくコンパイルできない場合があります。
そこで TypeScriptと JavaScriptとの橋渡しをするのが、型定義ファイルの役割です。型定義ファイルとは、名前の通り、型情報だけを定義したファイルのこと。型定義ファイルによって、 JavaScriptライブラリに型情報を与え、 TypeScriptが正しく認識(コンパイル)できるようにしてやるわけです。

TypeScriptとJavaScriptはスーパーセットとサブセットの関係にあるということで、ライブラリなども普通に使えると思っていたが、そういうわけでもないらしい。これをどのように扱うかについては、これまでさまざま苦労があったようだ。

qiita.com

テストを実行する

ツールを全て理解するのは困難だが、使うのは簡単だ。上のインストールさえ済んでしまえば、あとはテストとコードを書いて実行するだけだ。テスト側のコードは以下のようになった(『テスト駆動開発』第3章までの段階)。

import assert = require('assert');  // 公式ガイドに"CAUTION: don't use import 'assert' from 'assert'"とある
import { Dollar } from '../src/dollar';

describe('Moneyクラスのテスト', () => {

  it('$5 * 2 = $10', () => {
    const five: Dollar = new Dollar(5);
    let product: Dollar = five.times(2);
    assert(10 === product.amount);
  })

  it('Dollarの副作用がない', () => {
    const five: Dollar = new Dollar(5);
    let product: Dollar = five.times(2);
    assert(10 === product.amount);
    product = five.times(3);
    assert(15 === product.amount);
  })

  it('equals()メソッドが機能する', () => {
    assert(new Dollar(5).equals(new Dollar(5)));
    assert(!new Dollar(5).equals(new Dollar(6))); // 三角測量
  })

})

import ... = require('...') について

引っかかる場所があるとすれば、1行目のimport文だろうか。ReadMeに記載されているように、import ... from ...としてしまうと正しく動かない。

このimport ... = require( ... )という記法はTypeScript特有のものらしい。公式ドキュメントに"export = and import = require()" という項目があり、そこには以下のように書かれている。

When exporting a module using export =, TypeScript-specific import module = require("module") must be used to import the module.

モジュール側でexport =を使う場合はimport ... = require( ... )を使え、ということらしい。エクスポートする方法についても少し調べてみたが、どうも一筋縄では行かなさそうなので今回は調査を中断。CommonJSあたりの古い書き方、ということなのだろうか。

memememomo.hatenablog.com

実行コマンドをpackage.jsonに追加する

上の時点で、既にテストは動くようになっている。しかし、いちいちコマンドを叩くのは手間なので、例によってpackage.jsonのscriptにコマンドを書いておく。今回は、下記のように記載した。

  "scripts": {
    "mocha": "mocha --watch-extensions ts -w --require espower-typescript/guess test/**/*.ts"
  },

--watch-extensionsは、-wオプションをつけた際に監視対象にするファイルの拡張子を指定するものだ。デフォルトではjsファイルを監視対象としているので、jsファイルが存在しないプロジェクトの場合は-wオプションをつけても即座に終了してしまう。--watch-extensions tsとtsファイルを指定することによって、tsファイルが監視対象となり、-wオプションが期待通りに働くようになる。

また、--require espower-typescript/guess test/**/*.tsの部分は、

  • espower-typescriptのguessモジュールを利用すること(--require
  • テストファイルとして test配下のtsファイル全てを利用すること(test/**/*.ts

を指示している。JavaScriptのテストコードを実行するときは、末尾のファイル名指定は必要なかったが、それはmochaがデフォルトでtest配下のjsファイルを利用するようになっているからだった。今回はテストファイルもtsファイルであるから、明示的に指定しなければならない。**は再帰的に検索することを意味しており、--recursiveオプションと同じ効果を持っている。

テスト実行結果

あとはテストを実行するだけだ。npm run mochaでテストが実行される。実行結果は下記のようになる。

f:id:ky_yk_d:20181104022844p:plain

できた!

感想

JavaScriptからTypeScriptに変わっただけで、使うツールが同じなら環境構築も簡単だと思っていた。ところが、いざ始めてみると、JSのときは暗黙のうちにデフォルト設定の恩恵を受けていたことに気づいたり、TypeScriptという言語について勉強するきっかけになってよかった。

また、今回はコマンドのオプションが多かったが、調べながら進められた。プログラミングの勉強を始めたときに最初に習ったC言語で、当初は「おまじない」だと言われていた#include <stdio.h>の意味を少し進んでから理解したときの感覚に近いものがあり、懐かしかった。

思えば、気づかないうちに、全てのコード・全てのコマンドの意味を理解しながら進めようという姿勢を失いかけていたのかもしれない。もちろん、現実的には、全てを理解することはできず、調べてもすぐには分からないことに出会うことは避けられない。ただ、それを繰り返すなかで、学習性無気力になり、自ら調べることを放棄してしまうとしたら、エンジニアとしての死は間近だろう。

ここのところは技術記事にこだわらずにブログを書いていて、非常に自分としては手応えを感じているのだが、やはり技術的な内容のブログを書くことの効用は大きいな、と久し振りに実感した執筆だった。技術記事から逃げる形で「雑記」を書いているような形に陥ることがないように、下記のアドバイスを頭に置いておこう。