PuppetがRubyからClojureへ乗り換えていくようです。
そんなRubyistが他にもいるかもしれないので、Rubyist向けにClojureだとどう書くか、みたいなガイドを書いてみます。
イディオム
jnchitoさんのRubyイディオム記事をClojureで書くとどうなるか、まず説明したいと思います。
後置if で行数を減らす
send_mail_to(user) if user.active?
いきなりですが、Clojureには後置記法がありません。
(if (:active? user)
(send-mail-to user))
慣例的な書き方では、おしりの括弧は改行入れずにまとめて書くので、他の言語のifブロックよりも1行少なくなります。
if + notではなく、unlessを使う
user.destroy unless user.active?
Clojureにはこの目的の、if-not
とwhen-not
があります。
(if-not (:active? user)
(destroy user))
どうしてもunless
キーワードじゃなきゃヤダというのであれば、マクロを使ってunless
構文を作ることができます。
(defmacro unless [condition success-body & failure-body]
`(if-not ~condition ~success-body ~failure-body))
(unless (:active? user)
(destroy user))
三項演算子を使って行数を減らす
user.admin? ? "I appreciate for that." : "Thanks"
そもそも多項演算子がデフォなので、「こういう場合は三項演算子を使わない」とかそういう議論が要りません。
(if (:admin? user) "I appreciate for that." "Thanks.")
if文もただのリストなので、もとより三項演算子っぽいです。
代入してからifで存在を確認、をまとめて書く
if user = find_user
send_mail_to(user)
end
if-let
は値が束縛できれば、与えられた式を評価する関数です。まったくもって上記のrubyのコードを同じことが、スマートに実現できます。
(if-let [user (find-user)]
(send-mail-to user))
子どものオブジェクトが存在する場合にのみ、そのプロパティやメソッドを呼び出して条件を確認する、をひとつのifで書く
if parent.children && parent.children.singleton?
singleton = parent.children.first
send_mail_to(singleton)
end
Clojureのスレッディングマクロsome->
は与えられた式を順に評価しnil
になるとそこで停止するものです。Optionalを使ってやりたいであろうことが、このスレッディングマクロを使うとOptionalという概念を持ち込まずとも実現できます。
(if (some-> parent (.children) (.singleton?))
(send-mail-to (.. parent children first)))
メソッドの戻り値を返すときにreturnを使わない
def build_message(user)
message = 'hello'
message += '!!' if user.admin?
message
end
これはClojureの場合当たり前ですが、return
という概念がないので使えません。rubyと同じく、関数を評価した結果が返ります。
(defn build-message [user]
(str "hello" (when (:admin? user) "!!")))
定数はfreezeさせる
ADMIN_NAMES = ["Tom", "Alice"].freeze
ADMIN_NAMES << "Taro" # => RuntimeError: can't modify frozen Array
これはClojureの強みです。値は決して変更できないので、freezeするという概念がありません。
=> (def admin-names ["Tom" "Alice"])
#'user/admin-names
=> (conj admin-names "Taro")
["Tom" "Alice" "Taro"]
=> admin-names
["Tom" "Alice"]
このように、admin-names
に一度束縛した値はCollectionであっても変更はできません。変更しようとすると別の値が作られるだけです。
配列を作るとき、[ ]の代わりに%w( )、%i( )を使う
actions = %w(index new create)
残念ながらClojureにはこの機能はありません。しかしマクロがあります。
(defmacro %w [& words]
(vec (for [w words] (str w))))
(%w hoge fuga piyo)
=> ["hoge" "huga" "piyo"]
(あ、これはジョークです)
要素の順番に意味がある配列は、同時に別々の変数で受け取る
quotient, remainder = 14.divmod(3)
puts "商は#{quotient}" # => 商は4
puts "あまりは#{remainder}" # => あまりは2
Clojureにはこの目的で、デストラクティング構文があります。
(let [[quotient remainder] (divmod 14 3)]
(puts "商は" quotient)
(puts "あまりは" remainder))
これは強力で、複雑なデータ構造でも1回のletでそれぞれの変数に束縛できます。
(let [[[x1 y1][x2 y2]] [[1 2] [3 4]]]
[x1 y1 x2 y2])
=>[1 2 3 4]
Webフレームワーク
Railsのようなフルスタックなフレームワークはあまり流行してません。
Sinatra相当のcompojureと、Rack相当のringがメジャーです。
compojureに関しては、そこそこ記事もあります。http://qiita.com/search?q=compojure
(GET "/user/:id" [id]
(str "<h1>Hello user " id "</h1>"))
テンプレート
Rubyの主要テンプレートライブラリであるhamlは、とてもS式です。
!!!
%html{ :xmlns => "http://www.w3.org/1999/xhtml", :lang => "en", "xml:lang" => "en"}
%head
%title BoBlog
%meta{"http-equiv" => "Content-Type", :content => "text/html; charset=utf-8"}
%link{"rel" => "stylesheet", "href" => "main.css", "type" => "text/css"}
%body
#header
%h1 BoBlog
%h2 Bob's Blog
#content
- @entries.each do |entry|
.entry
%h3.title= entry.title
%p.date= entry.posted.strftime("%A, %B %d, %Y")
%p.body= entry.body
#footer
%p
All content copyright © Bob
これをClojureのもっともメジャーなHTML出力ライブラリであるhiccupを使うと、以下のように書けます。
(html5 {:xmlns "http://www.w3.org/199/xhtml" :lang "en" "xml:lang" "en"}
[:head
[:title "BoBlog"]
[:meta {:http-equiv "Content-Type" :content "text/html; charset=utf-8"}]
[:link {:rel "stylesheet" :href "main.css" :type "text/css"}]]
[:body
[:div#header
[:h1 "BoBlog"]
[:h2 "Bob's Blog"]]
[:div#content
(for [entry entries]
[:div.entry
[:h3.title (:title entry)]
[:p.date (:posted entry)]
[:p.body (strftime "%A, %B %d, %Y" (:body entry]]))]])]
同じですね。それでいてhiccupはClojureの式そのものなので思考の切り替えなく、業務ロジックとHTML出力を書くことができます。
お誘い
さて、少しでもClojureに興味を持っていただけたRubyistのみなさんに、ピッタリのイベントがあります。
10/10 19:00〜 Clojure夜会
http://01e8c979c4e57f83dd63bf3d4a.doorkeeper.jp/events/14626
ライトなClojureの話題が中心ですので、参加してみてください!