(Fun with)

Logo

Intro

razum2um

Vlad Bokov

@razum2um

ClojureScript

Lisp for JS

https://github.com/clojure/clojurescript
Parens
http://xkcd.com/297/

WTF

David Nolen

David Nolen

ClojureScript is a new compiler for Clojure that targets JavaScript
immutability@youtube
Rich Hickey

Rich Hickey

Clojure rocks, JavaScript reaches
simple made easy@infoq
transcripts@github

Looks like this

(defcomponent app [{:keys [title connected qr question answers]} _]
  (render [_]
    (html
      [:div.wrapper
       [:h1 title]
       (om/build question-widget question)
       (if slide?
         [:div
          [:p (str "Connected: " connected)]
          (om/build-all stat-widget answers)
          (om/build qr-widget qr)]
         (om/build-all answer-widget answers))]
      )))
                

Why not JS?

  • '1' == 1 // true 'true' == true // false

  • cljs.user=> (= 1 "1") ;; false

  • cljs.user=> (+ 1 "1") WARNING: cljs.core/+, all arguments must be numbers, got [number string] instead. at line 1 cljs repl "11"

Skill Test

  • [] + []
  • > ""

  • [] + {}
  • > "[object Object]"

  • {} + {}
  • > NaN
WAT
@destroyallsoftware (1:25-)
@habrahabr
wtfjs.com

JS forces you to be async

Callback Hell

even on server

Callback Hell

promises everywhere in ecosystem?

keystonejs/keystone (4.5K stars, 770 forks) on Mar 23, 2015

exports.signin = function(lookup, req, res, onSuccess, onFail) {
  if (!lookup) {
    return onFail(new Error('session.signin requires a User ID or Object as the first argument'));
  }
  var User = keystone.list(keystone.get('user model'));
  if ('string' === typeof lookup.email && 'string' === typeof lookup.password) {
    // match email address and password
    User.model.findOne({ email: lookup.email }).exec(function(err, user) {
      if (user) {
        user._.password.compare(lookup.password, function(err, isMatch) {
          if (!err && isMatch) {
            exports.signinWithUser(user, req, res, onSuccess);
          }
          else {
            onFail(err);
          }
        });
      } else {
        onFail(err);
      }
    });
  } else {
    lookup = '' + lookup;
    // match the userId, with optional password check
    var userId = (lookup.indexOf(':') > 0) ? lookup.substr(0, lookup.indexOf(':')) : lookup,
      passwordCheck = (lookup.indexOf(':') > 0) ? lookup.substr(lookup.indexOf(':') + 1) : false;
    User.model.findById(userId).exec(function(err, user) {
      if (user && (!passwordCheck || scmp(passwordCheck, hash(user.password)))) {
        exports.signinWithUser(user, req, res, onSuccess);
      } else {
        onFail(err);
      }
    });
  }
};
                

ES6 Features

ES6 ClojureScript
modules namespaces
var [one, ...rest] = foo;
var {foo, bar: baz} = qux;
var {foo, bar: baz} = qux;
[1, 2].map(x => x * 2)
[[one & rest] foo]
[{:keys [foo] baz :bar} qux]
[{:keys [foo] baz :bar} qux]
(map #(* % 2) [1, 2])
var, let, const let
iterator map (lazy), transducers (@youtube)
generator lazy-seq
type annotations core.typed
Map, Object {}, defrecord
JS ClojureScript
this
var that = this
explicit args passing, partial
(this-as that)
uglifyjs, yui-compressor Google Closure Compiler
JSHint, JSLint cljs.analyzer (works out-of-the-box)
undeclared Var .. at line, file
google closure compiler
macros? ;( (defmacro)
stdlib? :keyword, #{}, UUID
transit format
Google Closure Library
... :advanced dead code elimination
grunt/gulp/broccoli lein-cljsbuild

CLJ(S) Features

Russian community

(newbie-friendly)

In Action

ficus.io clone

https://github.com/razum2um/jsmeetupbrn

New Project

  • JDK
  • Leiningen
  • lein new app
  • [org.clojure/clojure "1.7.0"]
    [org.clojure/clojurescript "1.7.170"]
    [figwheel-sidecar "0.5.0-SNAPSHOT"]
    [org.omcljs/om "0.8.8"]
    [prismatic/om-tools "0.4.0"]
    [sablono "0.3.6"]
                        

Boilerplate

git checkout step1

Hotspots

  • css, html
  • cljc: state, atom
  • clj: components
  • cljs: html macros, app.js, dependencies

Push State

git checkout step2

  • cljc: thread-macros, swap
  • clj: custom component, add-watch

Connection screen

git checkout step3

  • clj: defonce, (p jsconf.state/state)
  • cljs: React lifecycle

Endpoints

git checkout step4

  • cljc: update-answer from console
  • clj: ring, middlewares
  • cljs: closure library

Poll

git checkout step5

  • cljs: optimistic update, my-votes
  • build