読者です 読者をやめる 読者になる 読者になる

スパゲティから学んだこと

f:id:bokuo-okubo:20150525094147j:plain

昔作ったiOSのコードをリファクタリングしたおはなし

昨年、開発メンバー二人で、iOSのアプリケーションをフルスクラッチで組みました。自分が大枠の設計、その後1.5ヶ月ぐらいをかけて二人で実装したものです。

書いた時の自分は、プログラマになって半年ぐらい、ろくなGUIプログラミング経験なし、といった有様で、設計手法、Objective-Cの言語仕様、Cocoaの使いかた,,,,etcを学びつつ生産するという割と無理ゲーなことをやっていました。また納期も短かったこともあり、とにかく動くもの作る、ということに注力していて、それはまあ美味しそうなスパゲティを生産しました。

そして、ここ最近、そのコードのリファクタリングをやっています。書いた当時の自分に比べ、半年くらいの時間しか経っていないのではありますが、その間に学んだことの量がハンパないので、リファクタリングを通して色々と気づいたところが多く、自分で思ったところをメモしておきます。

お品書き

  • したごしらえ
  • まずはレシピを整えよう
  • いざ調理
  • まとめと、参考になった本と読みたい本

□したごしらえ

へっぽこコーダーの僕たちは、マッチョなリファクタリング哲学を持ち合わせる脳みそもなく、 まずは現状に対してどうアプローチをとるべきなのか考えなければなりませんでした。

錯綜した仕様

プログラマ歴としてまだ日の浅かった当時の自分にとり、ゼロからアプリケーションの設計をすることは、今から思えばかなりハードルが高かったな、と思います。それでもそれなりにパッチワーク的に知識を集約し、なんとか完成までこぎつけ、現在も稼働するプロジェクトのはじめの道筋をつけるところまではできたわけです。

しかし、どんなに"良さ気"な設計ができていても、根本的にコードレベルでの実現力がヌルかったので、その設計方針をきちんと落としこむことができず、技術的負債を積み上げることとなりました。

当時の自分はUMLなんてわからないので仕様書も書けず、またテスト駆動の重要性/存在の理由について考えがまわっていなかったのでテストコードもありません。リファクタリングしていく前の状態は、テストコードも明文化された仕様もない、ただバージョン管理されたコードベースと生成物がある状態でした。

セオリー通りのリファクタリングができない

本来であれば現状の機能に対してユニットテストを書き、仕様を一旦フリーズさせてからリファクタリングに入るべきだと思います。

しかしながら、我々のコードでセオリーを実行していくには、問題点が多すぎました。

もともとのコードがクソ過ぎてユニットテスト書くのがダルすぎる!!!!!!!!!!!!!

  • 杜撰な命名のクラス/メソッドたち
  • 肥大した神ViewControllerたち
  • 果てしなく続く長大なメソッド
  • 杜撰なCRUD処理/サーバレスポンスのハンドリング
  • etc..

このような要因から、現状のグジャグジャの仕様に対してテストを書くのはだるすぎる、という話になり、-- (テストコードなしで)現状の仕様を崩さずリファクタリングしていく -- にはどうすればいいか、ということを検討しました。


□ まずはレシピを整えよう

相手にするコードがぐちょぐちょなだけに、リファクタリングしていく上でもうかつに手を出せませぬ。。 どうすれば効率的、可能な限り安全にリファクタリングできるか、というレシピを考えました。

リファクタリング方針

現状の仕様に対して、

  • 可能な限りドキュメントを作成する

要求仕様がそもそも明文化されていないので、コードから要求仕様をビルドアップしていく。

ドキュメント生成ツールを利用し、詳細仕様htmlを別リポジトリ管理とし、その更新作業をスクリプトで自動化しました。

仕様について、コーディング規約について、検討、検証しながら同時的に決めていく。

ある程度リファクタリングに関しての意識が足並みが揃った時点から、gitホスティングサービスを利用して、issue/プルリクエストを多用するチケット駆動体制にしていく。

というところを意識していこう、という方針でリファクタリングをはじめていきました。

仕様書生成/テストがらみ

Doxygenを使い、仕様書の生成を行いました。 また、別リポジトリで生成されたhtmlの管理を行い、 ビルドツール(rake)でそのフローを自動化しました。

リファクタリング済/新規実装のコードで、ビジネス層にあたる部分に関しては、なるべくテストコードを書くようにして...います。(あんまり実践できてない) SwiftのQuickをつかってみている。

ワークフローについて

お互い仕事の片手間にやっていることもあり、

  • 問題点
  • 書くべき/リファクタすべき機能
  • 書くべきコード

の直交性を意識してissueを発行し、プルリクエストベースで開発していくスタイルにしました。 週末や空き時間に、やんなきゃいけないことをちょこっとだけやる、って感じのスタイルにできて、よかったです。


□ いざ調理

プロジェクト全体として改善(広義のリファクタリング)すべき点は3点ありました。

  1. 設計レベルでの再設計
  2. コードレベルでの再設計(狭義のリファクタリング)
  3. 開発環境の移植性、テスト環境の構築

特に1.,2.に関しては、どちらの方向からも参照関係を持つので、問題解決の順番についてきちんと考慮しなければな、と思いました。

上記三点を同時並行的に進めていきましたが、はじめの部分に関して順序関係を入れて書いてみようと思います。

でかすぎるメソッドの分割、変数/メソッドのリネーム

プロジェクトを離れていたこともあり、僕自身はそもそも実装したコードが完全に脳みそのストレージから落ちていたので、まずはコードリーディングから入りました。

が、まぁ杜撰な命名、杜撰な設計なのでコードが読みにくい読みにくい。。

そこで、機能として代表的なクラスをいくつかにしぼり、ライブコーディングしながら仕様の確認をしていく、というペアプログラミングもどきのようなことを実践しました。具体的には、ほぼほぼ手続きの羅列として記述されていたメソッドの中身を、きちんと変数のスコープを確認しながら、機能性を意識した名前をつけたメソッドに分割していく作業を共同で行っていく感じです。

メソッドの分割で見えてくるもの

"語感のスコープ"があまりにも漠然としたプロシージャを、どのような操作の集まりなのかを意識した命名を行ったプロシージャとして定義しなおすことで、類似の操作を行うプロシージャのパターンをコードベース全体の中から沢山掬い出すことができました。

元々のコードは、似たようなViewの生成、通信機能などが画面や機能ごとに冗長に記述されていて、ものすごく凝集度が低かったです。。。また、本来別メソッドとして切り分け、条件分岐はメソッドレベルで切り替えるべきようなところも、長大なメソッドの中にそのままロジックが記述されていて、本当に恐ろしい状態でした。。。よく動いてたな、っていうレベル。

設計レベルでのリファクタリング

類似機能がある程度溜まってきたところで、新規クラスとして再設計するとか、アーキテクチャレベルでの機能の位置を変えて、委譲させるような形にできないかを考えました。

というか、今このフェーズでコード書いてる、ってかんじですね。




リファクタリングする際に役立った知識

このへんがものすごく役立ちました。

テストコードを書くようになった。

普段は僕はサーバ側の仕事をしていますが、このコードを書いた当時と違い、(アプリに対してぬるいわけではないんだけど)絶対に落とせない、という制約が強い中で仕事をして、きちんとテストコードを書く文化に触れました。

ユニットテストに関してのコードの保証云々、には色々な議論があると思うので、テストコード書かなきゃいけない主義、っていうのは行き過ぎかな、と思っているのですが、個人的にとても良いな、と思う点は、コードを見る視点が切り替えられる、ということです。 プロダクトコードを書いているときはどうしても機能の内側に自分の意識が入って、コードのなかをウニョウニョ漂っているので、実現すべき機能の最短ラインに落ちてしまいがちです。一方でテストコードを書いてるときは、そのプロダクトコードを外部から利用する形で書くので、別の方向からもういちどコードを眺め直すことができる。セルフチェックとしての点が結構強いのだな、と考えています。

デザインパターンを学習した

23種類全部やれたのか、というと半分ぐらいしかきちんと機能説明できないような気がしますが、 ともかくも、他のクラス/インスタンスから当該クラス/インスタンスを利用する、ということのバリエーションについて、いくつかきちんと紙に図示して学習しておいたことが役立ちました。

実際にパターンに落とす/落とせるかどうかはさておき、クラスレベルでの設計に関して見通しが良くなったということと、「インタフェイスに対してロジックを考える」ような、コードの凝集度を上げていくためのセンスが身についたと感じました。

関数型言語(もといLisp)を書くようになった

関数型言語、学び始めたばかりではありますが、LispHaskellを触ってから得られた感覚として、副作用があるのかないのか、ということをきちんと自覚しなければならない、ということを思い知りました。

オブジェクト指向関数型言語もきちんと理解できていないのに生半可なことは言えないです。という前置きつきですが、 オブジェクト指向に関しての色々な議論がある昨今ですが、GUIを扱うようなプログラミングではオブジェクトに状態を持てるということ、そしてなにより、Smalltalk流のメッセージパッシングは、抽象概念を具体性を持って考え、コードにしていく上でやっぱりわかりやすいと思っていて、オブジェクト指向はこれからも大切なんだろうな、って思い至っています。

ただ一方で、僕は当該のアプリケーションを書いたときに、副作用を持つ/持たないメソッドについてあまりにも無自覚でした。結構平気で返り値の無いメソッドの中に分岐入れてたりとかしていて、今考えると頭悪かったな、って思います。

余談ですがLisp書いてからObjective-Cのメッセージング式を見たら、ああかわいいなぁ、と思うようになりました。 とまれやっぱSwiftのほうが生産性高いと思います。Xcodeの機能がSwiftに対してはまだもっさりしてるのは気に食わないけど。

DRYを体現したい

(誤解を恐れず言うならば)所謂GoFデザインパターンは、クラスベースかつ静的型付け(寄り)の言語で、クラス/インスタンス レイヤでの抽象的な操作を実際のコードに落とすための方法論 だと思いました。ポリモフィズムの体現のための、エッセンス。

一方で関数型言語での関数設計は、 コードレイヤでの設計のお作法のエッセンス が詰まっているように感じます。

関数型なのか、オブジェクト指向なのか、というような選択的なことでなく、凝集度が高くて保守しやすいコードを書く、っていう共通の高みを目指してコードを書けるように意識したいな、って思いました。


□ まとめと、参考になった本と読みたい本

まとめ

実際にはまだコード書いている途中で、まだもう少し続きそうではあるのですが、 全体を通して感じていることとして、糞コード直すの楽しいな、って思ってます。笑

自分たちで自分たちに採点しているような感じで、昔の自分のセンスのなさに身悶えながらではありますが、

別のところで得てきた知識がどんなものだったか、というのが再確認されるような気がして、個人的には楽しんでいます。

参考になった本と読みたい本

-- 参考にしたやつ --

□ 詳解 Objective-C 2.0 第3版

使いこなしきれて無かったプロトコルの使い方とか、クロージャの使い方とか、結構見ました。 なんだかんだでランタイムと密に結合してる言語でかつ暗黙知が多い言語だとおもうので、黒本は持ってないとだめですね。

□ 独習デザインパターン

この手の本で手元にあったのがこれだけだったので。。 あとはwebで大体事足りますね。 パターンを実際にどう使うか、っていうところは感覚的に慣れるしかないんだろうな、と思いました。

□ リーダブルコード

名前重要だよ。っていう前半部分だけでも十分だと思います。 実は英語版しか読んでない。

-- これから読みたいやつ --

□ 新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)

おまえまず真っ先にこれ読めよ、って話なんでしょうけど読んでないというオチ。

□ エリック・エヴァンスドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

いろんなレイヤーのデザインがある中で、もっとアーキテクチャ設計に関しては広い知識を入れたいな、って思います。サーバ単体、アプリ単体のアプリケーション設計しかまだ到達してないんだもんなぁ.. エンタープライズ アプリケーションアーキテクチャパターン も読まなきゃね。

emacs上にClojureの開発環境を整えた。

emacs(もといrepl)は偉大

もともとはゆとり世代なので、emacsもviも使いこなしきれない情弱勢だったんですが。

CLの勉強をしていくなかで、emacs × slimeのコンボでコードをその場でreplで確認していくのに慣れてしまってからというものの、これがなしではいきられない体になってしまいました。

ということで、Clojureemacs上でのClojure開発環境を整える

んですが、emacs弱者なので、自分メモにすぎませんのであしからず。

流れ


  1. leiningenの導入
  2. Clojure-modeの導入
  3. Ciderの導入

1. Leiningenの導入

$ brew install leiningen

$ lein repl で対話環境が立ち上がればOKです。

Clojureの処理系はこれで一発で入ります。

2. Clojure-modeの導入

emacsclojureモードをいれます。

https://github.com/clojure-emacs/clojure-mode

パッケージインストールしてください。はい。

3. Ciderの導入

CLでいうところのSlimeみたいなもんみたいです。

Leiningenで動くClojureのREPL環境にポート接続でつなぎまする

https://github.com/clojure-emacs/cider

https://github.com/clojure/tools.nrepl

パッケージインストールしてください。はい。

Leiningenのprofileにもろもろグローバルに使うツールの指定をしないとプンスカされます。 いろいろ参考にして僕はこうしました。

{:user {:profiles {:dev {:global-vars {*warn-on-reflection* true}}}
        :jvm-opts ["-server" "-XX:+AggressiveOpts" "-XX:+TieredCompilation" "-Xverify:none"]
        :dependencies [[slamhound "RELEASE"]
                       [org.clojure/tools.trace "RELEASE"]
                       [org.clojure/tools.nrepl "0.2.7"]
                       [criterium "0.4.3"]]
        :aliases {"slamhound" ["run" "-m" "slam.hound"]}
        :plugins [[lein-kibit "RELEASE"]
                  [cider/cider-nrepl "0.9.0-SNAPSHOT"]]}}

emacs

M-x cider-jack-in

すると

f:id:bokuo-okubo:20150511040310j:plain

なんかnREPLがらみでwarning出てたような気がするのでまた書きますっておもったけど出てなかったからかかないとおもう

雑でござる

Clojure - Functional Programming for the JVM 続き(1)

シンタックスのところとか、試しながらやってるので、あってるかどうか知りません。

Getting Started ~Clojureをはじめよう〜

Clojure code for your own library and application projects will typically reside in its own directory (named after the project) and will be managed by the Leiningen project management tool. Leiningen (or "lein" for short) will take care of downloading Clojure for you and making it available to your projects. To start using Clojure, you don't need to install Clojure, nor deal with jar files or the java command — just install and use lein (instructions on the Leiningen homepage, linked to above).

あなたのライブラリなり、アプリケーションなり、Clojureソースコードは、プロジェクト名のついたディレクトリに格納される。また、Leiningenというプロジェクト管理ツールで管理されるよ。 Leiningen(または単にLein)はClojure処理系のダウンロード、プロジェクトごとに使えるものはどれか、ということをやってくれる。 Clojureを使いはじめるには、Clojure処理系を直接インストールしてくるとか、jarファイルを扱ったり、javaコマンドを打ったりする必要はないよ。ただ、Leinを使えばよいのである。

Once you've installed lein, create a trivial project to start playing around with:

Leinをインストールしたら、簡単なプロジェクトをつくってみよう。

cd ~/temp
lein new my-proj
cd my-proj
lein repl # starts up the interactive REPL

To create a new application project, do "lein new app my-app" For more about getting started, see http://dev.clojure.org/display/doc/Getting+Started.

新しくプロジェクトを作る際には、lein new app my-appとやればいいよ。 くわしくはこっちみてね

Clojure Syntax

Lisp dialects have a very simple, some would say beautiful, syntax. Data and code have the same representation, lists of lists that can be represented in memory quite naturally as a tree. (a b c) is a call to a function named a with arguments b and c. To make this data instead of code, the list needs to be quoted. '(a b c) or (quote (a b c)) is a list of the values a, b and c. That's it except for some special cases. The number of special cases there are depends on the dialect.

Lispの言い回しはとてもシンプルで、美しいとさえ言われるほど。 データとコードは同じ表現で、リストのリストはメモリの表現としても、単純なツリー構造だ。 (a b c)aという関数をbcという引数を伴って呼んでるっちゅうこと。 コードとしてではなく、データとしていまのを表現したいときは、クォートが必要だよ。 '(a b c)(quote (a b c))みたいに書いてね。こうすると、a,b,cという3つの値を持ったリストになるよ。

そうではない、特別な場合もあるから、注意しよう。 特別な場合っていうのは、Clojureの方言に依存するところだよ。

The special cases are seen by some as syntactic sugar. The more of them there are, the shorter certain kinds of code become and the more readers of the code have to learn and remember. It's a tricky balance. Many of them have an equivalent function name that can be used instead. I'll leave it to you to decide if Clojure has too much or too little syntactic sugar.

特別な場合っていうのは、糖衣構文として存在するよ。 それらのほとんどの場合っていうのは、よりコードを短く、コードを読む側の人間が理解しやすく、覚えやすいようになっているよ。 それは結構バランスの難しい問題だ。 糖衣構文によって短くするような構文の大多数は、代わりに使える他の関数が存在している。 そのシンタックスシュガーが良いものか、悪いものかの決断の余地を、ユーザに残しておくよ☆

The table below briefly describes each of the special cases encountered in Clojure code. These will be described in more detail later. Don't try to understand everything in the table now.

はい、基本的な特殊なシンタックスをみてきます。

--

  • ○ Purpose(目的)
  • ■ Suger(糖衣構文)
  • □ Function(関数本体)

--

  • ○ 一行コメント
  • ; text
  • (comment text)

--

  • ○ charリテラル
  • \char \tab \newline \space\unicode-hex-value
  • (char ascii-code) (char \uunicode)

--

  • ○string
  • "text"
  • (str char1 char2 ..)

--

  • ○ キーワード、埋め込み文字列?、同じオブジェクトを参照する同じ名前のキーワード、マップオブジェクトのキーとしてよく使われる
  • :name
  • (keyword "name")

--

  • ○ 対象の名前空間でかいけつされる名前
  • ::name
  • □ 対応する関数なし

  • 正規表現

  • #"pattern" 関数を使う時とクォートのルールが違う
  • (re-pattern pattern)

--

  • ○ ホワイトスペース。可読性向上のためにコレクションなどで使う
  • ,, (a comma)
  • □ 概念として存在しない

--

  • ○ リスト構文 [CLとおなじ]
  • '(items) 要素を評価しない
  • (list items) 要素を評価する

--

  • vector 配列に似ている
  • [items]
  • (vector items)

--

  • ○ set 集合
  • #{items} ハッシュ集合の生成
  • (hash-set items) (sorted-set items)

--

  • ○ map 連想配列
  • {key-value-pairs}
  • (hash-map key-value-pairs) (sorted-map key-value-pairs)

--

  • ○ シンボルやコレクションにメタデータを付加する
  • ^{key-value-pairs} object 読み取り時プロセス
  • (with-meta object metadata-map) ランタイムプロセス

--

  • ○ シンボルやコレクションからメタデータをマップとして取り出す
  • (meta object)

--

  • ○ 関数の引数の数だけ云々。よくわからんちん
  • gather a variable number of arguments in a function parameter list
  • ■ & name
  • □ N/A

--

  • ○ conventional name given to function parameters that aren't used
  • ■  _ (an underscore)
  • □ N/A

--

  • ○ construct a Java object; note the period after the class name
  • (class-name. args)
  • (new class-name args)

--

  • Javaメソッドを呼ぶ
  • ■ (. class-or-instance method-name args)or(.method-name class-or-instance args)
  • □ なし

--

  • ○ いくつかのJavaメソッドを、それぞれの項の要素の結果を、次の要素のはじめの引数としてマルチスレッディングで実行する?どのメソッドもカッコの中にいろいろ追加できる。..のいちに。(よくわかんない)
  • ■ (.. class-or-object (method1 args) (method2 args) ...)
  • □ none

--

  • ○ create an anonymous function
  • ■ #(single-expression) use % (same as %1), %1, %2 and so on for arguments
  • (fn [arg-names] expressions)

--

  • ○ 「レフ」「アトム」「エージェント」の参照
  • ■ @ref
  • □ (deref ref)

--

  • ○ シンボルの値のかわりに、Varオブジェクトを得る
  • #'name
  • (var name)

--

  • ○ バッククォート
  • ■ `
  • □ none

--

  • ○ アンクォート
  • ■ ~value
  • □ (unquote value)

--

--

  • ○ シンボルの自動作成
  • ■ prefix#
  • □ (gensym prefix?)

--

Lisp dialects use prefix notation rather than the typical infix notation used by most programming languages for binary operators such as + and *. For example, in Java one might write a + b + c, whereas in a Lisp dialect this becomes (+ a b c). One benefit of this notation is that any number of arguments can be specified without repeating the operator. Binary operators from other languages are Lisp functions that aren't restricted to two operands.

Lispの書き方では二項演算子について、ほとんどのプログラミング言語で見られる典型的な中置記法の代わりに、前置記法を使います。 例えば、Javaだとa + b + cと書かなければならないところを、Lispなら、(+ a b c)と書けます。 この記法の利点のひとつは、値の数が多くなっても、演算子をいちいち取り替えなくてすむことです。 他のプログラミング言語での二項演算子では2つしか置くことのできない演算子でも、Lispなら2つに縛られることはないのです!

One reason Lisp code contains more parentheses than code in other languages is that it also uses them where languages like Java use curly braces. For example, the statements in a Java method are inside curly braces, whereas the expressions in a Lisp function are inside the function definition which is surrounded by parentheses.

Lispが他の言語に比べて、沢山のカッコを用いいる理由のうちのひとつは、Javaのような言語で波括弧として使われているところでも丸括弧を使うからです。 例えば、Javaステートメントはブレースの中にありますが、Lispの場合はカッコのなか?

Compare the following snippets of Java and Clojure code that each define a simple function and invoke it. The output from both is "edray" and "orangeay".

ClojureJavaの2つの簡単な関数、処理のコードスニペットを見てくだせ。 どっちもアウトプットは、"edray"と"orangeay".


// This is Java code.
public class PigLatin {

    public static String pigLatin(String word) {
        char firstLetter = word.charAt(0);
        if ("aeiou".indexOf(firstLetter) != -1) return word + "ay";
        return word.substring(1) + firstLetter + "ay";
    }

    public static void main(String args[]) {
        System.out.println(pigLatin("red"));
        System.out.println(pigLatin("orange"));
    }
}
; This is Clojure code.
; When a set is used as a function, it returns the argument if it is
; in the set and nil otherwise.  When used in a boolean context,
; that indicates whether the argument is in the set.
(def vowel? (set "aeiou"))

(defn pig-latin [word] ; defines a function
  ; word is expected to be a string
  ; which can be treated like a sequence of characters.
  (let [first-letter (first word)] ; assigns a local binding
    (if (vowel? first-letter)
      (str word "ay") ; then part of if
      (str (subs word 1) first-letter "ay")))) ; else part of if

(println (pig-latin "red"))
(println (pig-latin "orange"))

Clojure supports all the common data types such as booleans (with literal values of true and false), integers, decimals, characters (see "character literal" in the table above) and strings. It also supports ratios which retain a numerator and denominator so numeric precision is not lost when they are used in calculations.

Clojureも、Javaが持っているようなboolとかintegerとかのデータ型を持っているよ。 分数云々だから計算によってあれして値がシカトされることもないよ。

Symbols are used to name things. These names are scoped in a namespace, either one that is specified or the default namespace. Symbols evaluate to their value. To access the Symbol object itself, it must be quoted.

シンボルはものに名付ける、ということに使われるよ。それらの名前は、指定した、またはデフォルトの名前空間の中にスコープをもつよ。 シンボルはその値に評価されるよ。シンボル、というデータそのものにアクセスしたいときは、クォートしなきゃだめや。

Keywords begin with a colon and are used as unique identifiers. Examples include keys in maps and enumerated values (such as :red, :green and :blue).

キーワードっていうのは、コロンから始まり、唯一のものじゃなきゃならん。 たとえば、マップのキーとか、列挙された値として使うよ。

It is possible in Clojure, as it is in any programming language, to write code that is difficult to understand. Following a few guidelines can make a big difference. Write short, well-focused functions to make them easier to read, test and reuse. Use the "extract method" refactoring pattern often. Deeply nested function calls can be hard to read. Limit this nesting where possible, often by using let to break complicated expressions into several less complicated expressions. Passing anonymous functions to named functions is common. However, avoid passing anonymous functions to other anonymous functions because such code is difficult to read.

Clojureであっても、他の言語と同様に、理解不能なわかりにくいコードを書くことはできる。 しかし、いくつかのガイドラインに従って書いていくことで、イイカンジにできるよ。 短く記述し、よく機能にフォーカスされた関数はそれを解読すること、テストすること、再利用することを簡単にするよね! ネストされまくったコードは読みづれえよ。 深いネストってのは大体いつも制御することが可能で、複雑な表現は、いくつかのもう少し複雑さが減少した表現に砕くことができる。 無名関数を有名関数にすることはよくあるテクニック しかしながら、無名関数をべつの無名関数に与えることは、チョー読みづらくなるから気をつけろ。

Clojure - Functional Programming for the JVM を読んでいくよ

Clojure programming

Clojure - Functional Programming for the JVM

http://java.ociweb.com/mark/clojure/article.html

自分が読みたいところしか訳さないです。

なんでもいいんだけどLisper、Java嫌いすぎだろ。草生えるわ。

そんなLisperの大嫌いなJavaVM上で動くClojureとか倒錯しすぎだろ。ツンデレか。。。

Functional Programming

Functional programming is a style of programming that emphasizes "first-class" functions that are "pure". It was inspired by ideas from the lambda calculus.

関数型プログラミングは関数が第一オブジェクトであり、かつ純粋であることを強調するよ。ラムダ計算に影響受けてるよ。

"Pure functions" are functions that always return the same result when passed the same arguments, as opposed to depending on state that can change with time.

「純粋な関数」とは同じ引数をとったら、いつも同じ結果を返すということだよ、タイミングによって変えられるような状態に依存しないかたちでね。

This makes them much easier to understand, debug and test.

この性質によって理解とデバッグとテストが簡単になるよ

They have no side effects such as changing global state or performing any kind of I/O, including file I/O and database updates.

グローバルな状態や、入出力(ファイルとかDBとか)に対して、副作用がないってことだよ

State is maintained in the values of function parameters saved on the stack (often placed there by recursive calls) rather than in global variables saved on the heap.

状態、は関数の値としてスタックに保存されるよ 主にそれは再帰呼び出しだよ ヒープには保存しなくてよいんだよ

This allows functions to be executed repeatedly without affecting global state (an important characteristic to consider when transactions are discussed later).

この特徴は関数の実行を何度もできるグローバルなStateに依存しないでね

It also opens the door for smart compilers to improve performance by automatically reordering and parallelizing code, although the latter is not yet common.

それによって、コンパイラが自動的に並列実行コードとしてパフォーマンス最適化してくれるからさ。そんなすぐできるもんでもないんだけど。

Data in functional programming languages is typically immutable.

関数型プログラミングは一般的に、イミュータブル。

This allows data to be accessed concurrently from multiple threads without locking. There's no need to lock data that can't be changed.

これによって、データロックなしに、マルチスレッドによる、データへの並行的なアクセスを可能にするよ。 データは変えられない(イミュータブル)だから、ロックする必要なんてないんだ。

With multicore processors becoming prevalent, this simplification of programming for concurrency is perhaps the biggest benefit of functional programming.

並列プログラミングに関する単純で、でも最も関数プログラミングの恩恵でもあるこの特徴によって、マルチコアプロセスを利用したプログラミングがもっと一般的になっていくよね。

Clojure Overview

Clojure is a dynamically-typed, functional language that runs on the JVM and provides interopareability with Java.

Clojureは、

  • 動的型付けで
  • 関数型で
  • JVM上で動いて、
  • Javaとの相互利用性を持った

言語だよ。

A major goal of the language is to make it easier to implement applications that access data from multiple threads(concurrency)

この言語の大きな目標は、並行性、並列性に関して簡単にすること。

Clojure is pronounced that same as the word "closure". Clojure は クロージャ って読んでね

the creator of the language, Rick Hickey explains the name this way :"I wanted to involve C(C#),L(Lisp), andJ(Java). Once Icame up with Clojure, given the punon clocure, the acailable domains and vast enptiness of the google space, it was an easy decision."

言語開発者であるRick Hickeyはこう言ってる。 「僕は、C(C#),L(Lisp), and J(Java)を含むような単語にしたかったんだ。punon clocureという言葉から一度Clojureという名前を思いついたら、ググラビリティも良かったし、ソッコー決めたよね。」

Clojure is also available for the .NET platform. ClojureCLR is an implementation of Clojure that runs on the Microsoft Common Language Runtime instead of the JVM. See https://github.com/clojure/clojure-clr.

Clojureは.NETでも動くよ。

In July 2011, ClojureScript was announced. It compiles Clojure code to JavaScript. See https://github.com/clojure/clojurescript.

ClojureScriptっていうAltJS的なやつもあるよ。

Clojure is an open source language released under the Eclipse Public License v 1.0 (EPL). This is a very liberal license. See http://www.eclipse.org/legal/eplfaq.php for more information.

ClojureEclipse Public Licence v1.0ですよ。

Running on the JVM provides portability, stability, performance and security. It also provides access to a wealth of existing Java libraries supporting functionality including file I/O, multithreading, database access, GUIs, web applications, and much more.

JVM上で動くってことは、

  • 環境移植性
  • 安定性
  • パフォーマンス
  • セキュリティー

を提供できるってことや。 そして、幸いな事にそれは、既存のJavaのライブラリの機能、file I/O, マルチスレッディング、データベースアクセス、GUIライブラリ、webアプリケーション、とか、色々にアクセスできるってことももたらしてくれてるっちゅうわけや。

Each "operation" in Clojure is implemented as either a function, macro or special form. Nearly all functions and macros are implemented in Clojure source code. The differences between functions and macros are explained later.

Clojureにおけるそれぞれの操作は、関数、マクロ、特殊形式として実装されるやで。 ほとんど全ての関数とマクロは、Clojureのソースとして実装されるもの。 マクロと関数の違いについてはあとで説明するやで。

Special forms are recognized by the Clojure compiler and not implemented in Clojure source code.

特殊形式は、クロージャソースコードとしては実装されないもので、Clojureコンパイラが認識するものなんや。

There are a relatively small number of special forms and new ones cannot be implemented. They include catch, def, do, dot ('.'), finally, fn, if, let, loop, monitor-enter, monitor-exit, new, quote, recur, set!, throw, try and var.

少数の特殊形式があって、特殊形式は新しく作ることができない。 例えば、catch, def, do, dot ('.'), finally, fn, if, let, loop, monitor-enter, monitor-exit, new, quote, recur, set!, throw, try and varみたいなものがそうだ。

Clojure provides many functions that make it easy to operate on "sequences" which are logical views of collections. Many things can be treated as sequences.

Clojureは、コレクションの論理的な見方で構成された、"sequence"というものにおいて簡単に操作できる沢山の関数を提供している。 多くのものはsequenceとして扱われる。

These include Java collections, Clojure-specific collections, strings, streams, directory structures and XML trees. New instances of Clojure collections can be created from existing ones in an efficient manner because they are persistent data structures.

それは、Javaのコレクション型、Clojure-specificなコレクション型、文字列、ストリーム、ディレクトリ構造、XMLツリーなんかを含む。 堅牢なデータ型であるかどうか、という効果的な決まり事に従って、Clojureの新しいコレクション型は作ることができる。

Clojure provides three ways of safely sharing mutable data, all of which use mutable references to immutable data.

Clojure は全てのイミュータブルなデータのミュータブルな参照に関して、安全な3つの方法を提供している。

Refs provide synchronous access to multiple pieces of shared data ("coordinated") by using Software Transactional Memory (STM).

「レフ(Ref)」は、データのいくつかの集まりに同期的にアクセスする方法を、ソフトウェアトランザクションメモリを用いて提供する。

Atoms provide synchronous access to a single piece of shared data.

「アトム」はひとつのシェアされたデータに同期的にアクセスする方法として提供される。

Agents provide asynchronous access to a single piece of shared data.

「エージェント」は、ひとつのシェアされたデータに非同期的にアクセスする方法として提供される。

These are discussed in more detail in the "Reference Types" section.

これらについては、より詳しくは "Reference Types" の項で触れる。

Clojure is a Lisp dialect. However, it makes some departures from older Lisps. For example, older Lisps use the car function to get the first item in a list. Clojure calls this first as does Common Lisp. For a list of other differences, see http://clojure.org/lisps.

ClojureLispの方言だよ。 だけど、よく知られたLisp表現から発展しているものもいくつかある。 たとえば、よく知られたLispでいうところのcarは、リストの一番初めの要素を取ってくる関数だ。 Clojureでは、firstというキーワードでそのCommon Lispの機能を表現する。
他の違いについてのリストは、これ.をみてね。

Lisp has a syntax that many people love ... and many people hate, mainly due to its use of parentheses and prefix notation. If you tend toward the latter camp, consider these facts.

Lispシンタックスは、多くの人に愛され,,,,多くの人に憎まれている。それは多くのところは、カッコの多用と、前置記法によるところが大きい。 もしあなたがその主張を推進する陣営なのだとしたら、現実について少し考慮しよう。

Many text editors and IDEs highlight matching parentheses, so it isn't necessary to count them in order to ensure they are balanced.

多くのテキストエディタIDEでは、カッコの自動補完をしてくれるから、カッコがきちんと閉じているかどうかいちいち数える必要というのはない。

Clojure function calls are less noisy than Java method calls. A Java method call looks like this:

Clojureの関数はJavaメソッドコールに比べて簡潔だ。 Javaメソッドコールはこんなかんじ。

methodName(arg1, arg2, arg3);

A Clojure function call looks like this:

クロージャだとこうなる。

(function-name arg1 arg2 arg3)

The open paren moves to the front and the commas and semicolon disappear. This syntax is referred to as a "form". There is simple beauty in the fact that everything in Lisp has this form. Note that the naming convention in Clojure is to use all lowercase with hyphens separating words in multi-word names, unlike the Java convention of using camelcase.

カッコのはじまりが、先頭に動き、コンマとセミコロンが消え去った!!!!!!!!!! このシンタックスは”フォーム”って呼ばれるよ。 全てのLispはこのフォームで動くよね、っていう事実は、簡潔な美しさを生む。 Clojureでの命名規則Javaのキャメルケースとちがって、全ての名前を小文字で書いて、ハイフンで分ける、っていうかんじ。

Defining functions is similarly less noisy in Clojure. The Clojure println function adds a space between the output from each of its arguments. To avoid this, pass the same arguments to the str function and pass its result to println.

関数定義も同様にClojureのほうが簡潔。 Clojureprintln関数は、出力したい項の間にそれぞれスペースを入れるだけでよい。 これを排除することによって、同じ項を持つ出力関数はprintlnをとおる

// Java
public void hello(String name) {
    System.out.println("Hello, " + name);
}
; Clojure
(defn hello [name]
  (println "Hello," name))

Clojure makes heavy use of lazy evaluation. This allows functions to be invoked only when their result is needed. "Lazy sequences" are collections of results that are not computed until needed. This supports the efficient creation of infinite collections.

Clojrueは遅延評価を多用する。 これはその値が必要になった時にその関数が実行するのを許す、ということだ。 "遅延 sequence"は必要になるまで計算されないものの集まりだ。 これは無限コレクションみたいのを作るのを容易にするよ。

Clojure code is processed in three phases: read-time, compile-time and run-time. At read-time the Reader reads source code and converts it to a data structure, mostly a list of lists of lists. At compile-time this data structure is converted to Java bytecode. At run-time the bytecode is executed. Functions are only invoked at run-time. Macros are special constructs whose invocation looks similar to that of functions, but are expanded into new Clojure code at compile-time.

Clojureのコードは3つの段階を経て実行される。

読み取り時というのは、Clojureのリーダがソースコードをよみ、リストのリストのリストというデータ構造に変換するときのことだ。 コンパイル時というのは、このデータ構造をJavaバイトコードに変換するときのこと。 ランタイム、というのは言わずもがな、実行じのことだ。 関数は、ランタイムでのみ実行される。 マクロは、一見関数と同じように実行されるように見えるが、コンパイル時に新しくClojureのコードとして展開される、特別な構造を持ったものだ。

Is Clojure code hard to understand? Imagine if every time you read Java source code and encountered syntax elements like if statements, for loops, and anonymous classes, you had to pause and puzzle over what they mean.

Clojureのコードを理解するのが難しいって? ステートメントコード、forループ、よくわかんないクラス、みたいな、Javaソースコードを読んでる時に遭遇する構文要素のことを想像してみなよ。 あなたは、一旦立ち止まり、それらがなにを意味しているのかいちいち考えなきゃいけないでしょ。

There are certain things that must be obvious to a person who wants to be a productive Java developer. Likewise there are parts of Clojure syntax that must be obvious for one to efficiently read and understand code. Examples include being comfortable with the use of let, apply, map, filter, reduce and anonymous functions ... all of which are described later.

それは生産的でありたいJavaの開発者にとって、深刻な問題であることは明白だよね。 Clojureシンタックスも同様に考えなきゃいけないけど、それが寄り効果的に読み進めることができて、コードの意味を理解しやすいことは明白さ。 let, apply, map, filter, reduce, その他の関数...これらがどんなに快適なものなのか、っていうことの実例は、これから説明していくよん!