(ns fun.clj
(:require-macros [klipse-clj.macros :refer-macros [dbg]])
(:require
[clojure.string :refer [join upper-case]]
[clojure.test
:refer-macros [deftest is are]]))
(ns fun.clj
(:require-macros [klipse-clj.macros :refer-macros [dbg]])
(:require
[clojure.string :refer [join upper-case]]
[clojure.test
:refer-macros [deftest is are]]))
Interactive Slides
Yehonathan Sharvit
(str (js/Date.))
A pragmatic theorist
A freak of interactivity
Author of Klipse: a client side code evaluator pluggable on any web page
Running a small consulting company
Sparking the joy of Clojure at Cycognito, Audyx and Nextjournal
Yehonathan Sharvit @viebel
You are motivated to learn Clojure
You look differently at your existing Software knowledge
You feel you had a fun time
You want to read my book on Clojure 馃捀
I wish I could express myself clearly
I wish I could code in a simple way
I wish I could write (and read) code that is predictable
I wish I could get more wishes
We cannot assign the result of an if
or a switch
statement to a variable.
var a = if (true) { 5 } else {42};
We cannot comment out an expression.
| The syntax is not uniform: operator, statements, function calls, function definition, classes…
|
Every part of the language is an expression, with the same structure |
arithmetic operation
| boolean operation
|
local bindings
| variable assignment
|
| function call
|
Javascript | Clojure |
|
|
|
|
|
|
|
|
An expression is enclosed into opening and closing parenthesis: ()
, []
or {}
First element inside the parentheses is the name of the expression: def
, if
, +
…
Other elements inside the parentheses are the arguments to be passed to the expression, either a primitive or an expression
When one of the arguments is itself an expression, it follows the same syntactic rules (recursively)
(defn foo [x y]
(if (= x (+ y y))
(* x y)
(inc x)))
(foo 42 20)
We can assign the result of an if
expression into a variable
(def my-mood (if my-num "happy" "sad"))
my-mood
We can comment out a single expression out of a big piece of code
(defn print-moods [num]
(println "happy")
#_(if (= num 42)
(println "furious")
(println "angry"))
(println "sad"))
(print-moods 42)
We can write code that receives source code and genenerates another code.
Challenge 1
Write a function that receives a
name
and prints to the console
Hello <name>!You may use the language of your choice.
function hello(name) {
console.log(`Hello, ${name}!`);
}
hello('Javascript');
Thoughts 馃
Can you write a unit test?
Can you compose?
Can you cache?
We are going to separate:
Computation (concatenation of "Hello"
and name
)
Side effects (print to the console)
We create a function named hello-str
that receives name
and "computes" the concatenation of "Hello"
and name
(defn hello-str [name]
(str "Hello, " name "!"))
The hello
function that does the side effect (print) uses hello-str
:
(defn hello [name]
(println (hello-str name)))
(hello "Clojure")
A pure function
A function with side effects
Testable
Composable
Cachable
A trivial test case
(deftest test-hello-clojure
(is (= (hello-str "Clojure")
"Hello, Clojure!")))
Check
(test-hello-clojure)
馃 Can you test the function you wrote to solve the challenge?
We can use our functions as building blocks 馃П for other functions.
We compose as we wish |
|
It returns a composed string |
|
We print it, later |
|
馃 Can you compose the function you wrote without modifying its code?
Handle sequences
Let’s handle sequence of names
(map hello-str ["Clojure" "Python" "Javascript" "Java"])
Let’s combine the elements of the sequence, into a string |
|
We print, later
(println (multiple-hellos ["Clojure" "Python" "Javascript" "Java"]))
Imagine that string concatenation were a heavy operation. |
|
We would like to cache the function
Next time we call it, it returns the result immediately
馃お It makes no sense to cache a function with side effects! |
|
Alonzo Church discovers the 位-calculus
Everything is an anonymous function with a single argument
No names in the language - only function argument
Even numbers are expressed as functions
0 := 位f位x.x
1 := 位f位x.fx
2 := 位f位x.f(fx)
John McCarthy invents LISP
It is the 1st FP language
The data is expressed as S-Expressions: (1 2 3)
, (a b (c d))
etc…
The code is made of S-Expressions: (+ 1 2 3)
, (if true 1 42)
etc…
Brendan Eich is recruited by Netscape to do "scheme in the browser"
Eventually, he invents Javascript
Functions are 1st class citizens
JSON data format is similar to S-Expressions
2007 - Rich Hickey invents Clojure - A practical dialect of LISP on top of JVM
2011 - ClojureScript - Clojure rocks, Javascript Reaches!
2013 - Facebook creates react.js - A functional javascript frontend framework
2015 - Dan Abramov invents redux - A javascript library that imposes FP constraints on a frontend app
Challenge 2
Read carefuly the following piece of code and tell me what is the value of x?
You may convert the code to the language of your choice.
function calcSomethingUseful(x) {
return Math.sin(x);
}
const x = 42;
calcSomethingUseful(x);
//x
Challenge 3
Read carefuly the following piece of code and tell me what is the value of y?
You may convert the code to the language of your choice.
function calcVeryUseful(arr) {
return arr.push(42);
}
const arr = [5, 99, 1230];
const z = calcVeryUseful(arr);
const y = arr.length;
//y
Immutability means that data collections are treated like values.
Values never change!
In the realm of numbers
| In the realm of strings
|
In the realm of vectors
| In the realm of maps
|
Code is predictable
No "by reference" or "by value" headache
Equality check is fast
Concurrency friendly wihtout locks
|
|
|
|
It would be cool to write asynchronous code that looks synchronous
It would be fun to add debugging capabilities
It would be productive to write code that disappears on production
It would be nice to pass data to data manipulation functions sequentially
(defn complex-stuff [x & forms]
(loop [x x forms forms]
(if forms
(let [form (dbg (first forms))
threaded (list (first form) (first (next form)) x)]
(recur threaded (next forms)))
x)))
(complex-stuff 10 [1 2] [4 5])
Wrapping functions become hard to read
| Use pipelines
|
It’s a macro
(macroexpand '(->>
(range 100)
(map inc)
(filter odd?)
(reduce +)))
(defn transform [x & forms]
(loop [x x forms forms]
(if forms
(let [form (first forms)
threaded (list (first form) (first (next form)) x)]
(recur threaded (next forms)))
x)))
(transform '(range 100)
'(map inc)
'(filter odd?)
'(reduce +))
I wish I could express myself clearly
The power of expressions
I wish I could code in a simple way
Pure functions
I wish I could write (and read) code that is predictable
Immutability
I wish I could get more wishes
Macros