ClojureでJavascriptが書けるClojureScriptが発表されました。(via Clojure/core — Introducing ClojureScript)

と思いかけましたが要はCoffeeScriptと同じアプローチで、

「オブジェクト指向なCoffeeScriptの代わりに関数型言語のClojure(ClojureScript)でJavascript書けるよ」

ということのようです。

とはいえClojureScriptはClojureの完全なJS実装というわけじゃなくて、JVMやJavaコアライブラリに依存してるところも多いので、Pure Clojureなら大体動く予定という感じのようです。

JavascriptとなるとGoogle Closureと名前が紛らわしいわ!という問題は承知してるそうですが、ClojureScript自体がGoogle Closure(Library, Compiler)に依存してるのでややこしいです。

インストール

% git clone git://github.com/clojure/clojurescript.git
% cd clojurescript
% script/bootstrap

ClojureScriptはClojureとJavascriptで書かれてるのでJava 6が必要です。(JavascriptにはRhinoがある)

REPL

% script/repljs
#'user/jse
"Type: " :cljs/quit " to quit"
ClojureScript:cljs.user> (println "foo")
foo
nil

Javascriptへのコンパイル

;; hello.cljs
(ns hello)
(defn ^:export greet [n]
  (str "Hello " n))
% bin/cljsc hello.cljs '{:optimizations :advanced}' > hello.js
;; hello.js
function b(c){throw c;}var f=null;function aa(){return function(c){return c}}function i(c){return function(){return this[c]}}function j(c){return function(){return c}}var k;
function n(c){var a=typeof c;if(a=="object")if(c){if(c instanceof Array)return"array";else if(c instanceof Object)return a;var d=Object.prototype.toString.call(c);if(d=="[object Window]")return"object";if(d=="[object Array]"||typeof c.length=="number"&&typeof c.splice!="undefined"&&typeof c.propertyIsEnumerable!="undefined"&&!c.propertyIsEnumerable("splice"))return"array";if(d=="[object Function]"||typeof c.call!="undefined"&&typeof c.propertyIsEnumerable!="undefined"&&!c.propertyIsEnumerable("call"))return"function"}else return"null";
else if(a=="function"&&typeof c.call=="undefined")return"object";return a}function ba(c){return typeof c=="string"}Math.floor(Math.random()*2147483648).toString(36);var ca={"\000":"\\0","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\x0B",'"':'\\"',"\\":"\\\\"},da={"'":"\\'"};
function ea(c){var m;c=String(c);if(c.quote)return c.quote();else{for(var a=['"'],d=0;d31&&g<127))if(e in da)e=da[e];else if(e in ca)m=da[e]=ca[e],e=m;else{g=e;o=e.charCodeAt(0);if(o>31&&o<127)g=e;else{if(o<256){if(g="\\x",o<16||o>256)g+="0"}else g="\\u",o<4096&&(g+="0");g+=o.toString(16).toUpperCase()}e=da[e]=g}o=e}h[l]=o}a.push('"');return a.join("")}}
function fa(c){for(var a=0,d=0;de;default:return c.apply(this,
arguments)}b("Invalid arity: "+arguments.length)};a.c=2;a.a=function(a){var e=K(a),g=K(M(a)),a=L(M(a));return c.call(f,e,g,a)};return a}();function gb(c){return eb.call(f,c,1)}function hb(c,a){return c^a}function ib(c,a){return c<>a}function kb(c){return O.call(f,0,c)}function lb(c,a){for(var d=a,e=J.call(f,c);;)if(s(function(){var a=e;return s(a)?kb.call(f,d):a}()))var g=gb.call(f,d),h=M.call(f,e),d=g,e=h;else return e}
x._=function(){return function(c,a,d){switch(arguments.length){case 2:var e;var g=lb.call(f,c,a);s(g)?e=K.call(f,g):b("Index out of bounds");return e;case 3:return e=lb.call(f,c,a),s(e)?K.call(f,e):d}b("Invalid arity: "+arguments.length)}}();
var Q=function(){function c(c,e){var g=I(Array.prototype.slice.call(arguments,1),0);return function(c,d){for(;;)if(s(d))var e=c.append(a.call(f,K.call(f,d))),g=M.call(f,d),c=e,d=g;else return a.call(f,c)}.call(f,new r(a.call(f,c)),g)}var a=f,a=function(a,e){switch(arguments.length){case 0:return"";case 1:return s(C.call(f,a))?"":a.toString();default:return c.apply(this,arguments)}b("Invalid arity: "+arguments.length)};a.c=1;a.a=function(a){var e=K(a),a=L(a);return c.call(f,e,a)};return a}(),mb=function(){return function(c,
a,d){switch(arguments.length){case 2:return c.substring(a);case 3:return c.substring(a,d)}b("Invalid arity: "+arguments.length)}}();function H(c,a){return Za.call(f,s(Ua.call(f,a))?function(){for(var d=J.call(f,c),e=J.call(f,a);;)if(s(C.call(f,d)))return C.call(f,e);else if(s(C.call(f,e)))return!1;else if(s(B.call(f,K.call(f,d),K.call(f,e))))d=M.call(f,d),e=M.call(f,e);else return s("\ufdd0'else")?!1:f}():f)}
function nb(c,a){return hb.call(f,c,db.call(f,a,2654435769,ib.call(f,c,6),jb.call(f,c,2)))}function R(c){return N.call(f,function(a,c){return nb.call(f,a,Ta.call(f,c))},Ta.call(f,K.call(f,c)),M.call(f,c))}function ob(c,a,d,e){this.b=c;this.C=a;this.z=d;this.count=e}k=ob.prototype;k.f=function(c,a){return H.call(f,c,a)};k.i=function(c,a){return new ob(this.b,a,c,E.call(f,this.count))};k.o=i("C");k.p=i("z");k.h=aa();k.m=!0;k.n=i("b");k.j=function(c){return R.call(f,c)};k.t=i("count");k.q=!0;
function pb(c){this.b=c}k=pb.prototype;k.f=function(c,a){return H.call(f,c,a)};k.i=function(c,a){return new ob(this.b,a,f,1)};k.o=j(f);k.p=j(f);k.h=j(f);k.m=!0;k.n=i("b");k.j=function(c){return R.call(f,c)};k.t=j(0);k.q=!0;var qb=new pb(f);function rb(c){return N.call(f,Oa,qb,c)}function D(c){var a=I(Array.prototype.slice.call(arguments,0),0);return N.call(f,Oa,qb,rb.call(f,a))}function sb(c,a,d){this.b=c;this.C=a;this.z=d}k=sb.prototype;k.h=aa();k.j=function(c){return R.call(f,c)};
k.f=function(c,a){return H.call(f,c,a)};k.q=!0;k.i=function(c,a){return new sb(f,a,c)};k.o=i("C");k.p=function(){return s(C.call(f,this.z))?qb:this.z};k.m=!0;k.n=i("b");function S(c,a){return new sb(f,c,a)}z.string=function(){return function(c,a,d){switch(arguments.length){case 2:return G.call(f,c,a);case 3:return G.call(f,c,a,d)}b("Invalid arity: "+arguments.length)}}();
y.string=function(){return function(c,a,d){switch(arguments.length){case 2:return x.call(f,c,a);case 3:return x.call(f,c,a,d)}b("Invalid arity: "+arguments.length)}}();x.string=function(){return function(c,a,d){switch(arguments.length){case 2:return s(O.call(f,a,w.call(f,c)))?c.charAt(a):f;case 3:return s(O.call(f,a,w.call(f,c)))?c.charAt(a):d}b("Invalid arity: "+arguments.length)}}();w.string=function(c){return c.length};Ca.string=function(c){return Ka.call(f,c,0)};
Aa.string=function(c){return fa.call(f,c)};this.String.prototype.call=function(){return function(c,a,d){switch(arguments.length){case 2:return Qa.call(f,a,this.toString());case 3:return Qa.call(f,a,this.toString(),d)}b("Invalid arity: "+arguments.length)}}();function tb(c){var a=c.x;return s(c.D)?a:(c.x=a.call(f),c.D=!0,c.x)}function T(c,a,d){this.b=c;this.D=a;this.x=d}k=T.prototype;k.h=function(c){return J.call(f,tb.call(f,c))};k.j=function(c){return R.call(f,c)};
k.f=function(c,a){return H.call(f,c,a)};k.q=!0;k.i=function(c,a){return S.call(f,a,c)};k.o=function(c){return K.call(f,tb.call(f,c))};k.p=function(c){return L.call(f,tb.call(f,c))};k.m=!0;k.n=i("b");function U(c){for(var a=v.call(f);;)if(s(J.call(f,c)))a.push(K.call(f,c)),c=M.call(f,c);else return a}function ub(c,a){for(var d=c,e=a,g=0;;)if(s(function(){var a=kb.call(f,e);return s(a)?J.call(f,d):a}()))var h=M.call(f,d),l=gb.call(f,e),g=E.call(f,g),d=h,e=l;else return g}
var wb=function vb(a){return s(C.call(f,a))?f:s(C.call(f,M.call(f,a)))?J.call(f,K.call(f,a)):s("\ufdd0'else")?S.call(f,K.call(f,a),vb.call(f,M.call(f,a))):f},xb=function(){function c(a,c,d){var e=I(Array.prototype.slice.call(arguments,2),0);return function t(a,c){return new T(f,!1,function(){var d=J.call(f,a);return s(d)?S.call(f,K.call(f,d),t.call(f,L.call(f,d),c)):s(c)?t.call(f,K.call(f,c),M.call(f,c)):f})}.call(f,g.call(f,a,c),e)}function a(a,c){return new T(f,!1,function(){var d=J.call(f,a);return s(d)?
S.call(f,K.call(f,d),g.call(f,L.call(f,d),c)):c})}function d(a){return new T(f,!1,function(){return a})}function e(){return new T(f,!1,j(f))}var g=f,g=function(g,l,o){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,g);case 2:return a.call(this,g,l);default:return c.apply(this,arguments)}b("Invalid arity: "+arguments.length)};g.c=2;g.a=function(a){var d=K(a),e=K(M(a)),a=L(M(a));return c.call(f,d,e,a)};return g}(),yb=function(){function c(a,c,g,h,l){var o=I(Array.prototype.slice.call(arguments,
4),0);return S.call(f,a,S.call(f,c,S.call(f,g,S.call(f,h,wb.call(f,o)))))}var a=f,a=function(a,e,g,h,l){switch(arguments.length){case 1:return J.call(f,a);case 2:return S.call(f,a,e);case 3:return S.call(f,a,S.call(f,e,g));case 4:return S.call(f,a,S.call(f,e,S.call(f,g,h)));default:return c.apply(this,arguments)}b("Invalid arity: "+arguments.length)};a.c=4;a.a=function(a){var e=K(a),g=K(M(a)),h=K(M(M(a))),l=K(M(M(M(a)))),a=L(M(M(M(a))));return c.call(f,e,g,h,l,a)};return a}(),zb=function(){function c(a,
c,g,h,l,o){var m=I(Array.prototype.slice.call(arguments,5),0),m=S.call(f,c,S.call(f,g,S.call(f,h,S.call(f,l,wb.call(f,m))))),p=a.c;return s(a.a)?s(P.call(f,ub.call(f,m,p),p))?a.apply(a,U.call(f,m)):a.a(m):a.apply(a,U.call(f,m))}var a=f,a=function(a,e,g,h,l,o){switch(arguments.length){case 2:var m=a,p=e,t=m.c;return s(m.a)?s(P.call(f,ub.call(f,p,t),t))?m.apply(m,U.call(f,p)):m.a(p):m.apply(m,U.call(f,p));case 3:return m=a,p=yb.call(f,e,g),t=m.c,s(m.a)?s(P.call(f,ub.call(f,p,t),t))?m.apply(m,U.call(f,
p)):m.a(p):m.apply(m,U.call(f,p));case 4:return m=a,p=yb.call(f,e,g,h),t=m.c,s(m.a)?s(P.call(f,ub.call(f,p,t),t))?m.apply(m,U.call(f,p)):m.a(p):m.apply(m,U.call(f,p));case 5:return m=a,p=yb.call(f,e,g,h,l),t=m.c,s(m.a)?s(P.call(f,ub.call(f,p,t),t))?m.apply(m,U.call(f,p)):m.a(p):m.apply(m,U.call(f,p));default:return c.apply(this,arguments)}b("Invalid arity: "+arguments.length)};a.c=5;a.a=function(a){var e=K(a),g=K(M(a)),h=K(M(M(a))),l=K(M(M(M(a)))),o=K(M(M(M(M(a))))),a=L(M(M(M(M(a)))));return c.call(f,
e,g,h,l,o,a)};return a}();function Ab(c,a){for(;;)if(s(C.call(f,J.call(f,a))))return!0;else if(s(c.call(f,K.call(f,a))))var d=c,e=M.call(f,a),c=d,a=e;else return s("\ufdd0'else")?!1:f}function Bb(c){return c}
var V=function(){function c(a,c,d,e,p){var t=I(Array.prototype.slice.call(arguments,4),0);return g.call(f,function(c){return zb.call(f,a,c)},function Nb(a){return new T(f,!1,function(){var c=g.call(f,J,a);return s(Ab.call(f,Bb,c))?S.call(f,g.call(f,K,c),Nb.call(f,g.call(f,L,c))):f})}.call(f,Oa.call(f,t,e,d,c)))}function a(a,c,d,e){return new T(f,!1,function(){var p=J.call(f,c),t=J.call(f,d),Ia=J.call(f,e);return s(s(p)?s(t)?Ia:t:p)?S.call(f,a.call(f,K.call(f,p),K.call(f,t),K.call(f,Ia)),g.call(f,
a,L.call(f,p),L.call(f,t),L.call(f,Ia))):f})}function d(a,c,d){return new T(f,!1,function(){var e=J.call(f,c),p=J.call(f,d);return s(s(e)?p:e)?S.call(f,a.call(f,K.call(f,e),K.call(f,p)),g.call(f,a,L.call(f,e),L.call(f,p))):f})}function e(a,c){return new T(f,!1,function(){var d=J.call(f,c);return s(d)?S.call(f,a.call(f,K.call(f,d)),g.call(f,a,L.call(f,d))):f})}var g=f,g=function(g,l,o,m,p){switch(arguments.length){case 2:return e.call(this,g,l);case 3:return d.call(this,g,l,o);case 4:return a.call(this,
g,l,o,m);default:return c.apply(this,arguments)}b("Invalid arity: "+arguments.length)};g.c=4;g.a=function(a){var d=K(a),e=K(M(a)),g=K(M(M(a))),p=K(M(M(M(a)))),a=L(M(M(M(a))));return c.call(f,d,e,g,p,a)};return g}(),Db=function Cb(a,d){return new T(f,!1,function(){if(s(kb.call(f,a))){var e=J.call(f,d);return s(e)?S.call(f,K.call(f,e),Cb.call(f,gb.call(f,a),L.call(f,e))):f}else return f})};
function Eb(c,a){function d(a,c){for(;;){var d=J.call(f,c);if(s(function(){var c=kb.call(f,a);return s(c)?d:c}()))var l=gb.call(f,a),o=L.call(f,d),a=l,c=o;else return d}}return new T(f,!1,function(){return d.call(f,c,a)})}
var Fb=function(){function c(c){return new T(f,!1,function(){return S.call(f,c,a.call(f,c))})}var a=f;return a=function(d,e){switch(arguments.length){case 1:return c.call(this,d);case 2:return Db.call(f,d,a.call(f,e))}b("Invalid arity: "+arguments.length)}}(),Gb=function(){function c(a,c,h){var l=I(Array.prototype.slice.call(arguments,2),0);return new T(f,!1,function(){var h=V.call(f,J,Oa.call(f,l,c,a));return s(Ab.call(f,Bb,h))?xb.call(f,V.call(f,K,h),zb.call(f,d,V.call(f,L,h))):f})}function a(a,
c){return new T(f,!1,function(){var h=J.call(f,a),l=J.call(f,c);return s(s(h)?l:h)?S.call(f,K.call(f,h),S.call(f,K.call(f,l),d.call(f,L.call(f,h),L.call(f,l)))):f})}var d=f,d=function(d,g,h){switch(arguments.length){case 2:return a.call(this,d,g);default:return c.apply(this,arguments)}b("Invalid arity: "+arguments.length)};d.c=2;d.a=function(a){var d=K(a),h=K(M(a)),a=L(M(a));return c.call(f,d,h,a)};return d}();function Hb(c,a){return Eb.call(f,1,Gb.call(f,Fb.call(f,c),a))}
function Ib(c){return function d(c,g){return new T(f,!1,function(){var h=J.call(f,c);return s(h)?S.call(f,K.call(f,h),d.call(f,L.call(f,h),g)):s(J.call(f,g))?d.call(f,K.call(f,g),L.call(f,g)):f})}.call(f,f,c)}
var Jb=function(){function c(a,c,g){var h=I(Array.prototype.slice.call(arguments,2),0);return Ib.call(f,zb.call(f,V,a,c,h))}var a=f,a=function(a,e,g){switch(arguments.length){case 2:return Ib.call(f,V.call(f,a,e));default:return c.apply(this,arguments)}b("Invalid arity: "+arguments.length)};a.c=2;a.a=function(a){var e=K(a),g=K(M(a)),a=L(M(a));return c.call(f,e,g,a)};return a}(),Kb=function(){function c(a,c,h,l){return new T(f,!1,function(){var o=J.call(f,l);if(s(o)){var m=Db.call(f,a,o);return s(B.call(f,
a,Pa.call(f,m)))?S.call(f,m,d.call(f,a,c,h,Eb.call(f,c,o))):D.call(f,Db.call(f,a,xb.call(f,m,h)))}else return f})}function a(a,c,h){return new T(f,!1,function(){var l=J.call(f,h);if(s(l)){var o=Db.call(f,a,l);return s(B.call(f,a,Pa.call(f,o)))?S.call(f,o,d.call(f,a,c,Eb.call(f,c,l))):f}else return f})}var d=f;return d=function(e,g,h,l){switch(arguments.length){case 2:return d.call(f,e,e,g);case 3:return a.call(this,e,g,h);case 4:return c.call(this,e,g,h,l)}b("Invalid arity: "+arguments.length)}}();
function W(c,a){this.b=c;this.d=a}k=W.prototype;k.f=function(c,a){return H.call(f,c,a)};k.i=function(c,a){var d=ia.call(f,this.d);d.push(a);return new W(this.b,d)};k.h=function(){var c=this;return s(fb.call(f,c.d.length,0))?function d(e){return new T(f,!1,function(){return s(O.call(f,e,c.d.length))?S.call(f,c.d[e],d.call(f,E.call(f,e))):f})}.call(f,0):f};k.G=!0;
k.w=function(){function c(a,c,g){var h=this;return s(function(){var a=P.call(f,0,c);return s(a)?O.call(f,c,h.d.length):a}())?h.d[c]:g}function a(a,c){var g=this;return s(function(){var a=P.call(f,0,c);return s(a)?O.call(f,c,g.d.length):a}())?g.d[c]:f}return function(d,e,g){switch(arguments.length){case 2:return a.call(this,0,e);case 3:return c.call(this,0,e,g)}b("Invalid arity: "+arguments.length)}}();k.m=!0;k.n=i("b");
k.v=function(){return function(c,a,d){switch(arguments.length){case 2:return G.call(f,this.d,a);case 3:return G.call(f,this.d,a,d)}b("Invalid arity: "+arguments.length)}}();k.j=function(c){return R.call(f,c)};k.t=function(){return this.d.length};k.q=!0;k.B=function(c,a,d){c=ia.call(f,this.d);c[a]=d;return new W(this.b,c)};k.u=function(){return function(c,a,d){switch(arguments.length){case 2:return x.call(f,c,a,f);case 3:return x.call(f,c,a,d)}b("Invalid arity: "+arguments.length)}}();
var Lb=new W(f,v.call(f));function Mb(c){return new W(f,c)}W.prototype.call=function(){return function(c,a,d){switch(arguments.length){case 2:return y.call(f,this,a);case 3:return y.call(f,this,a,d)}b("Invalid arity: "+arguments.length)}}();function Ob(c){return N.call(f,Oa,Lb,c)}function Pb(){}Pb.prototype.f=j(!1);var Qb=new Pb;
function Rb(c,a){return Za.call(f,s(Va.call(f,a))?s(B.call(f,Pa.call(f,c),Pa.call(f,a)))?Ab.call(f,Bb,V.call(f,function(c){return B.call(f,Qa.call(f,a,K.call(f,c),Qb),La.call(f,c))},c)):f:f)}function Sb(c,a,d){for(var e=d.length,g=0;;)if(s(O.call(f,g,e)))if(s(B.call(f,a,d[g])))return g;else g=db.call(f,g,c);else return f}v.call(f);function X(c,a,d){this.b=c;this.count=a;this.r=d}k=X.prototype;k.f=function(c,a){return Rb.call(f,c,a)};
k.i=function(c,a){return s(Wa.call(f,a))?ra.call(f,c,x.call(f,a,0),x.call(f,a,1)):N.call(f,la,c,a)};k.h=function(){var c=this;if(s(kb.call(f,c.count))){var a=Xa.call(f,c.r);return Jb.call(f,function(a){return V.call(f,Ob,Kb.call(f,2,c.r[a]))},a)}else return f};k.F=!0;k.m=!0;k.n=i("b");k.j=function(c){return R.call(f,c)};k.t=i("count");
k.B=function(c,a,d){var c=Ta.call(f,a),e=this.r[c];if(s(e)){var e=ia.call(f,e),g=ha.call(f,this.r);g[c]=e;c=Sb.call(f,2,a,e);return s(c)?(e[E.call(f,c)]=d,new X(this.b,this.count,g)):(e.push(a,d),new X(this.b,E.call(f,this.count),g))}else return e=ha.call(f,this.r),e[c]=v.call(f,a,d),new X(this.b,E.call(f,this.count),e)};
k.u=function(){return function(c,a,d){switch(arguments.length){case 2:return y.call(f,c,a,f);case 3:var e=this.r[Ta.call(f,a)],g=s(e)?Sb.call(f,2,a,e):f;return s(g)?e[E.call(f,g)]:d}b("Invalid arity: "+arguments.length)}}();var Tb=new X(f,0,function(){return{}}.call(f));X.prototype.call=function(){return function(c,a,d){switch(arguments.length){case 2:return y.call(f,this,a);case 3:return y.call(f,this,a,d)}b("Invalid arity: "+arguments.length)}}();
function Ha(c){for(var a=I(Array.prototype.slice.call(arguments,0),0),a=J.call(f,a),d=Tb;;)if(s(a))var e=Ma.call(f,a),d=Ra.call(f,d,K.call(f,a),La.call(f,a)),a=e;else return d}Ha.call(f);function Ub(c){if(s($a.call(f,c)))return c;else if(s(function(){var a=ab.call(f,c);return s(a)?a:bb.call(f,c)}())){var a=c.lastIndexOf("/");return s(O.call(f,a,0))?mb.call(f,c,2):mb.call(f,c,E.call(f,a))}else return f}
function Vb(c){if(s(function(){var a=ab.call(f,c);return s(a)?a:bb.call(f,c)}())){var a=c.lastIndexOf("/");return s(fb.call(f,a,-1))?mb.call(f,c,2,a):f}else return"\ufdd0'else"}function Y(c,a,d,e,g,h){return xb.call(f,Mb([a]),Ib.call(f,Hb.call(f,Mb([d]),V.call(f,function(a){return c.call(f,a,g)},h))),Mb([e]))}
var Z=function Wb(a,d){return s(C.call(f,a))?D.call(f,"nil"):s(Ya.call(f,a))?D.call(f,"#"):s("\ufdd0'else")?xb.call(f,s(function(){var e=Qa.call(f,d,"\ufdd0'meta");return s(e)?(e=s(s(a)?a.m:a)?!0:u.call(f,ua,a),s(e)?Sa.call(f,a):e):e}())?xb.call(f,Mb(["^"]),Wb.call(f,Sa.call(f,a),d),Mb([" "])):f,s(s(s(a)?a.k:a)?!0:u.call(f,A,a))?Fa.call(f,a,d):D.call(f,"#<",Q.call(f,a),">")):f};X.prototype.k=!0;
X.prototype.g=function(c,a){return Y.call(f,function(c){return Y.call(f,Z,""," ","",a,c)},"{",", ","}",a,c)};A.number=!0;Fa.number=function(c){return D.call(f,Q.call(f,c))};Ja.prototype.k=!0;Ja.prototype.g=function(c,a){return Y.call(f,Z,"("," ",")",a,c)};T.prototype.k=!0;T.prototype.g=function(c,a){return Y.call(f,Z,"("," ",")",a,c)};A["boolean"]=!0;Fa["boolean"]=function(c){return D.call(f,Q.call(f,c))};A.string=!0;
Fa.string=function(c,a){return s(ab.call(f,c))?D.call(f,Q.call(f,":",function(){var a=Vb.call(f,c);return s(a)?Q.call(f,a,"/"):f}(),Ub.call(f,c))):s(bb.call(f,c))?D.call(f,Q.call(f,function(){var a=Vb.call(f,c);return s(a)?Q.call(f,a,"/"):f}(),Ub.call(f,c))):s("\ufdd0'else")?D.call(f,s("\ufdd0'readably".call(f,a))?ea.call(f,c):c):f};W.prototype.k=!0;W.prototype.g=function(c,a){return Y.call(f,Z,"["," ","]",a,c)};ob.prototype.k=!0;ob.prototype.g=function(c,a){return Y.call(f,Z,"("," ",")",a,c)};
A.array=!0;Fa.array=function(c,a){return Y.call(f,Z,"#",a,c)};pb.prototype.k=!0;pb.prototype.g=function(){return D.call(f,"()")};sb.prototype.k=!0;sb.prototype.g=function(c,a){return Y.call(f,Z,"("," ",")",a,c)};function Xb(c){return Q.call(f,"Hello ",c)}var Yb="hello.greet".split("."),$=this;!(Yb[0]in $)&&$.execScript&&$.execScript("var "+Yb[0]);for(var Zb;Yb.length&&(Zb=Yb.shift());)!Yb.length&&Xb!==void 0?$[Zb]=Xb:$=$[Zb]?$[Zb]:$[Zb]={};

Webでの実行

<html>
  <head></head>
  <body>
    <script type="text/javascript" src="hello.js"></script>
    <script>
      alert(hello.greet("ClojureScript"))
    </script>
  </body>
</html>

感想

お、動くじゃん。

ただ、CoffeeScriptの利点は吐かれるJSもかなり可読性が高いものになる(それこそ俺が適当に手書きしたJSより)というところだと思うので、元々関数型で結構書きやすいJSを関数型言語からイミフなJSにコンパイルするのはちょっとなーという感じです。モジュール性を高めたい場合にはunderscore.js等を使ってJSネイティブで関数型っぽく書く方が僕は惹かれています。

ClojureScriptをnodejsで動かすって部分は更にイミフです。サーバーサイドだったらClojureそのもので書いたらえんちゃうの?

JavascriptはWebのアセンブラだ!っていう考えは今後主流になったりするんでしょうかね。どうなんでしょうかね。

最初からlisp環境には"あきらめが鬼なっていた"vimmerだがclojure環境はスゴイ揃ってる。

EvalToplevel

<LocalLeader>et

カーソルがあるとこのトップレベルのS式をreplに送る。

EvalFile

<LocalLeader>ef

ファイル全体をreplに送る。

EvalBlock

<LocalLeader>eb

VISUALモードで選択した部分をreplに送る。

EvalLine

<LocalLeader>el

カーソルのある行をreplに送る。

EvalParagraph

<LocalLeader>ep

カーソルのあるカッコ内をreplに送る。

RequireFile

<LocalLeader>rf

ファイルを:reloadフラグを付けて読み込む。

RequireFileAll

<LocalLeader>rF

ファイルを:reload-allフラグを付けて読み込む。

RunTests

<LocalLeader>rt

ファイルを:reloadフラグを付けて読み込んで、clojure.contrib.test-isのrun-testsでテスト実行する。

MacroExpand

<LocalLeader>me

カーソルのある一番内側のマクロを展開する。

MacroExpand1

<LocalLeader>m1

MacroExpandと同じだけどmacroexpand-1を使う。(一回だけ展開する奴)

DocLookupWord

<LocalLeader>lw

カーソルのあるとこのwordのdocを探して表示する。

DocLookupInteractive

<LocalLeader>li

プロンプトに任意の文字を入れてdocを探して表示する。

FindDoc

<LocalLeader>fd

入力したパターンを元にdocを探して表示する。(find-doc)

JavadocLookupWord

<LocalLeader>jw

カーソルのあるとこのwordのjavadocを外部ブラウザで開く。

JavadocLookupInteractive

<LocalLeader>ji

プロンプトに任意の文字を入れてjavadocを探して外部ブラウザで表示する。

SourceLookupWord

<LocalLeader>sw

カーソルのあるとこのwordのソースを表示する。ソースはCLASSPATHが通ってる必要がある。

SourceLookupInteractive

<LocalLeader>si

プロンプトに任意の文字を入れてソースを探して表示する。ソースはCLASSPATHが通ってる必要がある。

GotoSourceWord

<LocalLeader>gw

カーソルのある場所のwordのソースに飛ぶ。ソースはPATHが通ってる必要がある。CLOJURE_SOURCE_DIRS環境変数でPATHに追加しておく。

GotoSourceInteractive

<LocalLeader>gi

プロンプトに任意の文字を入れてソースに飛ぶ。ソースはPATHが通ってる必要がある。CLOJURE_SOURCE_DIRS環境変数でPATHに追加しておく。

MetaLookupWord

<LocalLeader>mw

カーソルのある場所のwordのメタデータを表示する。

MetaLookupInteractive

<LocalLeader>mi

プロンプトに任意の文字を入れてメタデータを表示する。

StartRepl

<LocalLeader>sr

新しいバッファに新しいreplをスタートする。同時に複数のreplがあるかも。

StartLocalRepl

<LocalLeader>sR

新しいバッファに新しいreplをスタートする。現在のバッファのnamespaceで初期化される。

AddToLispWords

<LocalLeader>aw

カーソルのある場所のwordをバッファのlispwordsオプションに追加する。これはフォームのインデントに使われる。

先週の土曜日(6月25日)にtokyo.clj#12に行ってきました。

場所は四谷にある株式会社タイムインターメディア様の会議室。(下がオライリー・ジャパン)

自分のブログによるとclojureをはじめたのが6月15日らしいので初心者ですが、初心者の時こそ得る物が多いと思って行ってみました。

僕はHSQLDBを使ってみたりスクリプトとして使う時の設定を探したりしてました。Hackthonなのでもくもくと作業できてよかったです。その後の懇親会もLisp/Clojure関係のお話を聞いてWeb/Ruby関係とはまた違った感じで新鮮で勉強になりました。

Clojureはまだ初心者ですが、Javaのライブラリがそのまま呼び出せる・呼び出しやすい為もあってか、周辺ツールが充実してる気がします。LispなのでみんなEmacsで書くため、CLI中心で行けるのも嬉しいです。(僕はvimですがVimClojureも期待より全然完成度高かったです)

JavaはIDEとXMLが障害となって避けてたんですが、Clojureにはその二つが苦手な人が沢山いるらしく、IDEをとXMLを使わなくていいツールを勝手に揃えてくれるので安心です。

ただ、カッコがついたファイルを「DSLだから設定ファイルとして使え」と言われても僕には未だにプログラムとしてしか見れないです。(project.cljとか.emacsとか)

下記の通りで何の問題も無し。

DBを使いたい - 逆引きClojure

HSQLDBの使い方でちょっと時間かかったぐらい。CLOB型とか覚えられない・・・。

ちゃんと書く場合はleiningenでproject作った方が楽だけど、手元で手軽にclojureしたい場合の為にsystem-wideにインストールする。

$ brew install clojure-contrib
# ~/.zshrc
export CLASSPATH=$CLASSPATH:/Users/komagata/homebrew/Cellar/clojure-contrib/1.2.0/clojure-contrib.jar

clojureも依存で入る。バージョンは2011/06/25現在だと1.2.0。ついでにちょっとした起動スクリプトもcljって名前で入れてくれる。

% clj
Clojure 1.2.0
user=> (+ 1 2 3)
6

引数無しだとreplが立ち上がる。

;; unk.clj
(println "unk")
% clj unk.clj
unk

スクリプトを渡せば実行される。

clojure.mainが標準入力からスクリプトを受け取れれば

#!clj
(println "unk")

って書けそうだけど、無理。

;; project.clj
(defproject bar "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.2.1"]]
  :main bar.core)

:mainでnamespaceを指定しておくと$ lein run -m bar.coreみたいにイチイチ指定しなくていい。

;; src/bar/core.clj 
(ns bar.core
  (:gen-class))

(defn -main []
  (println "unk"))

スタンドアローンで実行できるjarを作るには:gen-classの指定と-main関数が必要。

まずそのまま実行してみる。

% lein run
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
unk

(このJAVAのOPTIONがイチイチ表示されるの何とかならないかな・・・)

% lein uberjar

Twitter / @mreinsch: @komagata yes, German "übe ...

ドイツ人の@mreinschさんによるとuberとはbeyondとかaboveの意味だそうです。(それでもよく分からないけど)

bar-1.0.0-SNAPSHOT-standalone.jarとbar-1.0.0-SNAPSHOT.jarが出来ていました。jar tvfで中身を見てみると、無印の方はこのプロジェクトのモノだけ。-standaloneの方は依存jar(clojure自身を含む)が全部入ったやつみたいです。

% java -jar bar-1.0.0-SNAPSHOT-standalone.jar 
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
unk

OMG!

Windows XPに持って行って実行してみると・・・

windows_xp_ie6 [Running]

(^q^)

HerokuのCedar StackはClojureに対応してる。要はLeiningenに対応してる。(RubyのBundlerに対応してるのと同じようなもん)

project.cljを見て必要なもんをpush時にインストールしてくれる。

;; lib/unk/core.clj
(ns unk.core
  (:use compojure.core
        ring.adapter.jetty))

(defroutes app
  (GET "/" _ "unk"))

(defn -main []
  (let [port (Integer/parseInt (get (System/getenv) "PORT" "8080"))]
    (run-jetty app {:port port})))
;; project.clj
(defproject unk "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [compojure "0.6.3"]            
                 [ring/ring-jetty-adapter "0.3.8"]])
# Procfile:
web: lein run -m unk.core
$ heroku apps:create --stack cedar
$ git push heroku master
$ heroku apps:open

http://sharp-wind-970.herokuapp.com/

OMG!

環境変数$PORTを使う必要がある部分でちょっと躓きました。

clojureのweb framework。

(ns foo.core
  (:use compojure.core))

(defroutes app
  (GET "/" _ "foo"))
(defproject foo "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [compojure "0.6.3"]]           
  :dev-dependencies [[lein-ring "0.4.3"]]
  :ring {:handler foo.core/app})
$ lein ring server

http://localhost:3000/

lein-ringで前回書いてた問題は解決。

ringはrubyでいえばrack。

(ns unk.core
  (:use ring.adapter.jetty))

(defn app [req]
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body "unk"})

(defn -main []
  (run-jetty app {:port 3000}))
$ lein run -m unk.core
2011-06-19 14:43:10.189:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
2011-06-19 14:43:10.191:INFO::jetty-6.1.26
2011-06-19 14:43:10.265:INFO::Started SocketConnector@0.0.0.0:3000

http://localhost:3000/

OMG!

ring.util.responseを使えばもうちょっと楽に書ける。

(ns unk.core
  (:use ring.util.response
        ring.adapter.jetty))

(defn app [req]
  (response "unk"))

(defn -main []
  (run-jetty app {:port 3000}))

run-jettyの部分がserver依存なので切り離す必要があると思う。どうやるのが普通なんだろう?

clojars.orgを検索した感じだとring server(紛らわしい名前だな)としてadapterがあって使えるそうなのはjetty, mongrel2, httpcoreぐらい。

  • leiningen = rubygems + bundler
  • clojars.org = rubygems.org
  • nailgun = SWANK
  • vimclojure = SLIME

Clojure + Leiningen

leiningen入れるとclojureも入る。

% curl -O https://raw.github.com/technomancy/leiningen/stable/bin/lein
% chmod +x lein
% lein repl
Using JLine for console I/O; install rlwrap for optimum experience.
REPL started; server listening on localhost:29973.

rlwrap入れたほうが良いと出るので入れる。

% brew install rlwrap
% lein repl
REPL started; server listening on localhost:58951.
user=> 1234
1234
user=> (+ 1 2)
3
user=> (exit)

うん。

VimClojure

vimの場合はVimClojureというのが定番ぽい。VimClojureはSyntax Highlightingとかだけでも使えるんだけど、NailgunというCLで言えばSWANK的なものへのインターフェースが同伴されていて、clojureファイルを開くと同時にNailgun Serverに接続することが出来る。

Nailgun ServerもClientも別個にインストールする必要がある。

% vi ~/.vimrc
Bundle 'VimClojure'
let vimclojure#WantNailgun = 1
let vimclojure#NailgunClient = "ng"
:BundleInstall

Nailgun Client

% brew install nailgun

ngコマンドが使えるようになる。(上記のNailgunClientで指定してるのがコレ)

Nailgun Server

% lein plugin install org.clojars.autre/lein-vimclojure 1.0.0

lein plugin install xxx versionはleinのプラグインをインストールするのであって、ライブラリを入れてる訳じゃない。両方共clojars.orgにホストされてるので混同しないようにする。(依存ライブラリはproject.cljに書いて後述するlein depsで入れる)

lein helpするとlein vimclojureというサブコマンドが増えてる。

helloworld

% lein new helloworld
% cd helloworld
% tree
.
├── README
├── classes
├── lib
├── project.clj
├── src
│   └── helloworld
│       └── core.clj
└── test
    └── helloworld
        └── test
            └── core.clj
% lein deps

project.cljに書かれてるライブラリ(clojure処理系自身を含む)を取ってきてlibに入れてくれる。

% lein vimclojure
NGServer started on 127.0.0.1, port 2113.
% vi src/helloworld/core.clj

Terminal — vim — 80×24

あとはreplが開いてSLIMEみたいに評価できる。