Getting started with Clojure

Embed Size (px)

Citation preview

Getting started with (Clojure)

- or how I learned to stop worrying and love the (function)

Its a strange kind of love...

Clojure is very differentPart of your brain may rebel !!

Homo-Iconic

List based

Immutable state

Dynamically typed

Tiny syntax

Infinitely extensible with Macros

What is Clojure

Functional programming on the JVM

A better Lisp ?

Why get functional ?

Clock speeds stopped getting faster around 2005Cant get around the speed of silicon switches

Moores law still in effectMore cores added every 18 months

Laptops with 128 cores by 2020 ??

Concurrency at the hardware levelNot just multi-threading

You may end up working here...

Why a better Lisp ?

Clojure is easier to understand Nicer libraries

Great interoperability with Java platform

Closer to pure functional languageExplicitly define mutable state

STM transactional memory

Clojure has a programmatic macro system which allows the compiler to be extended by user code

You can add your own language features with macros. Clojure itself is built out of macros such as defstruct:

(defstruct person :first-name :last-name)

If you need different semantics, write your own macro. If you want a variant of structs with strong typing and configurable null-checking for all fields, you can create your own defrecord macro, to be used like this:

(defrecord person [String :first-name String :last-name] :allow-nulls false)This ability to reprogram the language from within the language is the unique advantage of Lisp. You will see facets of this idea described in various ways: Lisp is homoiconic - Lisp code is just Lisp data. This makes it easy for programs to write other programs.The whole language is there, all the time. Paul Grahams essay Revenge of the Nerds explains why this is so powerful. http://www.paulgraham.com/icad.html

Lisp syntax also eliminates rules for operator precedence and associativity, with fully parenthesized expressions, there is no possible ambiguity

Classic or Re-Imagined

Lisp

Clojure

Why create Clojure

Concurrency in Java / OO is challengingMutable state-full paradigm

Fast enough persistent data structures made it viable

Functions as first class Functions part of data structure

Functions do not have side effects

Focus on computation (maths) rather than procedural algorithms

Hickey's primary interest was concurrency he wanted the ability to write multi-threaded applications, but increasingly found the mutable, stateful paradigm of object oriented programming to be part of the problem

The idea of a functional Lisp integrated with a commercially accepted host platform just seemed like chocolate and peanut butter. Coming up with persistent data structures that were fast enough was the tipping point for my considering it viable.

functions as first-class objects, meaning that functions can be placed into data structures, passed as arguments to other functions, evaluated in comparisons, even returned as the return value of another function. Moreover, functions do not have "side effects" the ability to modify program state or data. This paradigm focuses on computation in the mathematical sense, rather than procedural algorithms, and is a completely different approach to programming.

Clojure does provide persistent data structures For application developers, the most significant distinction is that Clojure defaults to making all data structures immutable

developers must use one of four special mutable structures that are explicitly designed to be shared between threads: refs, vars, atoms, and agents. Clojure uses software transactional memory (STM) to coordinate changing these mutable structures while keeping them in a consistent state, much like a transactional database. This model makes it considerably simpler to write thread-safe code than it is in object oriented languages. No locks are required, therefore there are no deadlocks or race conditions.

Why use Clojure

Its a pure functional programming language

You can use existing Java code and platform

Simple syntax

It gets you thinking differently !!!

An excuse to learn Emacs properly ??

Throw away your knowledge about OO and try something different

The downside of Clojure

( x )

The downside of Lisps simple, regular syntax, at least for beginners, is Lisps fixation on parentheses and on lists as the core data type. Clojure offers an interesting combination of features that makes Lisp more approachable for non-Lispers.

The downside of Clojure (2)

( ( x ) )

The downside of Lisps simple, regular syntax, at least for beginners, is Lisps fixation on parentheses and on lists as the core data type. Clojure offers an interesting combination of features that makes Lisp more approachable for non-Lispers.

The downside of Clojure (3)

( ( ( x ) ) )

The downside of Lisps simple, regular syntax, at least for beginners, is Lisps fixation on parentheses and on lists as the core data type. Clojure offers an interesting combination of features that makes Lisp more approachable for non-Lispers.

The downside of Clojure (4)

( ( ( ( x ) ) ) )

The downside of Lisps simple, regular syntax, at least for beginners, is Lisps fixation on parentheses and on lists as the core data type. Clojure offers an interesting combination of features that makes Lisp more approachable for non-Lispers.

The downside of Clojure (...)

( ( ( ( ( x ) ) ) ) )

The downside of Lisps simple, regular syntax, at least for beginners, is Lisps fixation on parentheses and on lists as the core data type. Clojure offers an interesting combination of features that makes Lisp more approachable for non-Lispers.

Tool support

Emacsclojure-mode, clojure-test, paredit-mode

Netbeansenclojure

IntelliJLa Clojure

EclipseCounterclockwise plugin

Build tools

Leiningen

Emacs + Slime

Cake

Maven

Lets look at Clojure code

We're not in Kansas any more...

Javapackage ;class ;member variables;access retType methodName (param, param) {}

Clojure(ns name-space-name)(defstruct my-data-struture :label-name)(functionName param (fn param)); param's can be functions too !!

Its just a tree...

a tree structure

Functions are data

Data structures are functions !!

Download

clojure.org

Or via buld toolMaven

Leiningen

Cake

Java At least version 5

Version 6 better performance and reporting

All hail the REPL

An interactive shell for clojure

Using Leiningen (Line ing en)

https://github.com/technomancy/leiningen/leinlein repl

Leiningen Clojure project

lein new

lein deps

lein repl

lein swank

Create a new clojure project

Download clojure

Start the interactive shell

Start repl server for emacs

Leiningen project file

(defproject my-jax-london-project "1.0.0-SNAPSHOT" :description "A meaningful description" :dependencies [[org.clojure/clojure "1.2.1"] [org.clojure/clojure-contrib "1.2.0"]] :dev-dependencies [[swank-clojure "1.2.1"] [org.clojars.rayne/autodoc "0.8.0-SNAPSHOT"]] :autodoc { :name "London Clojure dojo", :page-title "Dojo API"}

;; Only re-fetch deps when they change in project.clj or when :library-path directory is empty. :checksum-deps true :license {:name "Eclipse Public License - v 1.0" :url "http://www.eclipse.org/legal/epl-v10.html" :distribution :repo :comments "..."} )

Loading code into the REPL

(load-file "temp.clj")

Stuff too big to type

use an absolute path or a path relative to where you launched the REPL

Use Emacs or other IDE when you're ready

Simplest possible examples

(* 2 2)(+ 1 2 3)(\ 24 4 3 2)(\ 2 4)(\ 2.0 4)(+ (* 4 5) 22)(+ 4 (* 3 2) 7)(+ 3 (* 2 (- 7 2) 4) (/ 16 4))

Note: prefix notation

Calling Java... ooooo!!

(javax.swing.JOptionPane/ showMessageDialog nil "Hello World" )

Ratio

Basic data type

Allow delaying computation

Avoid loss of precision

(/ 2 4)(/ 2.0 4)(/ 1 3)(/ 1.0 3)(class (/ 1 3)

True or false

(> 1 2 3)(< 1 2 3)(> 2 5 4)(< 2 5 4)

Simple function example

(defn hello-world [name] (println(str "Hello " name)))

(hello-world "jr0cket")

What class is that...

(class (str "Jr0cket"))java.lang.String

(class (defn hello-world [name] (str "Hello cruel world")))clojure.lang.Var

str

(str \h \e \l \l \o)

Concatenate strings together

Can represent a character using \

Booleans / Expressions

(= 1 1.0)(= 1 2)(< 1 2)

True is a symbol, but alsouser=> (class true)java.lang.Boolean

(if 0 (println True))(if nil (println True))(if (println True))

More examples

(last [1 1 2 3 5 8])

(defn penultimate [x] (last (butlast x)) )

(penultimate [1 2 3 4 5])

(doc last)

(doc butlast)

And more...

(nth [1 1 2 3 5 8] 2)

(count [1 1 2 3 5 8])

(reverse [1 1 2 3 5 8])

(defn palindrome? [x] (= x (reverse x)) )

Proposition naming convention

Even more

(flatten [[1 1] 2 [3 [5 8]]])

(compress "aaaabccaadeeee")

(encode "aaaabccaadeeee")

(replicate 10 "a")

Where to find out more...

http://clojure.org/cheatsheet

http://clojure.github.com/clojure/clojure.core-api.html

Your own functions

Define your own algorithms

(defn square [x] (* x x))

Anonymous functions

(fn )

(# )

(def sqr #(* % %))

Overloading functions

(defn make ([ ] ; the make function that takes no arguments (struct vector 0 0)) ([x y] ; ... takes x and y keywords as arguments (struct vector x y)))

Pure functions no side effects

Clojure functions are purethey have no side effects

Unless you define them as such

Pure functions are easy to develop, test, and understandAim for pure functions where possible

Clojure data structures

( Lists ) - Ordered collection of elements(list 1 3 5) '(8 13 21)

{ map }

[ Vectors ] - Optimised for random access[:tom :dick :harry]

Lists are for code, Vectors for data(nth [:tom :dick :jane :harry ] 2)

List operations

(first 1 2 3)The head of the list

(last 7 8 9)The last element of the list

(rest 1 2 3 4 5)Everything but the head

(cons :new-list '(1 2 3 4 5))New list, given head and tail

More data structures...

(defstruct date :day :month :year)

(struct date)

as we did not specify any parameters, we just get nil values

things in curly brackets are hash maps - the usual Java hashmaps

maps

{ :a 1 :b 2}

user=> { :a 1 :b 2}

{:a 1, :b 2}

user=> { :a 1 :b }

java.lang.ArrayIndexOutOfBoundsException: 3

user=> { :a 1 :b 2}

{:a 1, :b 2}

user=> { :a 1 :b 3} ; this should make the repl complain in clojure 1.2, fine in 1.1

{:a 1, :b 3}

user=> {:a {:a 1}}

{:a {:a 1}}

user=> {{:a 1} :a}

{{:a 1} :a}

; idiom - put :a on the left

Vectors

[:neo :morpheus :trinity :smith]

[:matrix-characters [:neo :morpheus :trinity :smith]]

(first [:neo :morpheus :trinity :smith])

(nth [:matrix :babylon5 :firefly :stargate] 2)

(concat [:neo] [:trinity])

(def my-vector

(vector? x)

Your own data structures

Special forms

(def johnny {:first-name "John", :last-name "Stevenson"})

(defstruct person :first-name :last-name)(defrecord person [String :first-name String :last-name] :allow-nulls false)

Memory use

Once all references to an immutable structure disappears it can be garbage collected.

Loops that create intermittent structures are garbage collected every turn of the loop.;;Memory : 0(let [a (range 50000)]) ;; Memory: "big" while the let is "executing";;Memory : 0 -- no reference to a anymore !

macros

Define extensions to the language

Clojure only has 7 primitive functionsEverything else in the language is created with macros

Allows the language to be extended easily without changes to the compiler

What are the 7 primitive functions?

Special forms

Recognized by the Clojure compiler and not implemented in Clojure source code. A relatively small number of special forms

New ones cannot be implemented

catch, def, do, dot ('.'), finally, fn, if, let, loop, monitor-enter, monitor-exit, new, quote, recur, set!, throw, try and var

if

user=> (doc if)-------------------------ifSpecial Form Please see http://clojure.org/special_forms#ifnil

Sequences

Sequences are logical views of collectionsLogical lists

Java collections, Clojure-specific collections, strings, streams, directory structures and XML trees.

New Clojure collections created efficientlyCreates a sort of branch (delta) in the data structure tree

Working with Sequences

first

rest

cons

Software Transactional Memory

Works like transactional databases

Provides safe, concurrent access to memory

Agents allow encapsulated access to mutable resources

Sharing mutable data

Use mutable references to immutable data

Reference Typessynchronous access to multiple pieces of shared data ("coordinated") by using STM

Atoms synchronous access to a single piece of shared data.

Agents asynchronous access to a single piece of shared data

Name-spaces

Define a namespace(ns name-space-name)

Include namespace code(use 'names-space-name)

Like a package statement in Java

When you require a library named clojure.contrib.str-utils, Clojure looks fora file named clojure/contrib/str-utils.clj on the CLASSPATH

To avoid having to use the namespace for your library, you have to use refer, like so - (refer 'examples/introduction)

The use function does both require refer, like so (use 'examples.introduction)

o force a library to reload:(use :reload-all 'examples.introduction)

The :reload-all flag is useful if you are making changes and want to see results without restarting the REPL.

Clojure Libraries

(use 'clojure.contrib.str-utils)

' Dont treat the next thing as a function

Open source libraries - http://clojars.org/

Recursive functions

Functions that call themselves

Fractal coding

Tail recursion

Avoids blowing the heap

A trick as the JVM does not support tail recursion directly :-(

Tail recursion

(defn factorial [x] (if (= x 0) 1 (* x (factorial (- x 1)))))

Dont blow your stack !!

This is barfing because the evaluator has to keep around state for each call due to the expression (* x (factorial (- x 1))) . We need to make this function tail recursive.

recur can be thought of as the Clojure operator for looping. Think of it like a function call for the nearest enclosing let or function definition supplied with new variables. Naively we can switch over to using this by doing:

user> (defn factorial2 [x] (if (= x 0) 1 (* x (recur (- x 1)))))

But this is a compile-time error (which in itself is pretty neat!).java.lang.UnsupportedOperationException: Can only recur from tail position (NO_SOURCE_FILE:4)

An accumulator parameter is an extra parameter to a function that's used to gather intermediate parts of the calculation. If we do this, we can make sure that the recur call is in the tail position. Using an anonymous function we get:

(defn factorial3 [x] ((fn [x y] (if (= x 0) y (recur (- x 1) (* x y)))) x 1))

Now when recur is used, it doesn't need to keep any of the previous stack frame around. This means we can finally calculate factorial 1000000, which begins with 282 and ends with lots of zeros!

TDD with Clojure is nice

Clojure test(deftest test-name (is (= value (function params))) )

Simple test

(ns simple-test (:use clojure.test) (:use simple))

(deftest simple-test (is (= (hello) "Hello world!")) (is (= (hello "test") "Hello test!")))

Working with Java

Java Classes fullstop after class name

(JFrame. )

(Math/cos 3) ; static method call

Java methodsfullstop before method name

(.getContentPane frame) ;;method name first

(. frame getContentPane) ;;object first

Importing

(ns drawing-demo (:import [javax.swing JPanel JFrame] [java.awt Dimension]))

Working with Java (2)

Clojure gives you clean, simple, direct access to Javacall any Java API directly

(System/getProperties)

-> {java.runtime.name=Java(TM) SE Runtime Environment

More (Java)

Simple functions for implementing Java interfaces and subclassing Java classes

Clojure functions all implement Callable and Runnabletrivial to pass the anonymous function to the constructor for a Java Thread.

(.start (new Thread (fn [] (println "Hello" (Thread/currentThread)))))

| Hello #

The # is Clojures way of printing a Java instance. Thread is the

class name of the instance, and Thread[Thread-0,5,main] is the instances

toString representation.

Calling Clojure from Java

Export the clojure to a .jar

Add the jar to the classpath

Import the library in your code

Call it like any other method

Errors are inevitable

In the REPL(printStackTrace *e)

*e holds the last exception raised

Clojure exceptions are Java exceptions

Managing State in Immutable world

Mutable data structures to share between threads (Software Transactional Memory)refs, vars, atoms, agents

No locks required for thread safe code, no deadlocks or race conditions

Atomically apply changes

Mutable functions

Swap!

Name functions that have side effects with an exclamation markNaming convention

Deployment

lein jar

lein uberjar

Documentation

(doc function-name)

(javadoc class-name)

(defn function-name A meaningful description of the function params )

Show fn description

Show javadoc in browser

Write documentation for your own functions

Example documentation

(doc str)Use doc to print the documentation for str:user=> (doc str)-------------------------clojure.core/str([] [x] [x & ys])With no args, returns the empty string. With one arg x, returns x.toString(). (str nil) returns the empty string. With more than one arg, returns the concatenation of the str values of the args.

Fully qualified namespace

Arguments

Details

Use doc to print the documentation for str:user=> (doc str)-------------------------clojure.core/str([] [x] [x & ys])

With no args, returns the empty string. With one arg x, returnsx.toString().

(str nil) returns the empty string.

With more than one arg, returns the concatenation of the str values of the args.

The first line of docs output contains the fully qualified name of thefunction.

The next line contains the possible argument lists, generated directly from the code. (Some common argument names and their uses are explained in the sidebar on the following page.)

Finally, the remaining lines contain the functions doc-string, if the function definition included one.

find-doc

(find-doc reduce)user=> (find-doc "reduce" )-------------------------clojure/areduce([a idx ret init expr])Macro... details ...-------------------------clojure/reduce([f coll] [f val coll])... details ...

Search for functions you dont know

Keyword parameter

Autodoc

Add dependency to your build filehttp://clojars.org/org.clojars.rayne/autodoc

lein deps

lein autodoc

Generate a website for your API'shttp://tomfaulhaber.github.com/autodoc/

Where next

Coding dojo London / start your ownwww.londonjavacommunity.co.uk

Books Programming Clojure (Pragmatic)

Website clojure.org dev.clojure.org

Full Disclojure vimeo.com/channels/fulldisclojure

clojure.jr0cket.co.uk

99 problems in clojure

Credits

No parentheses were harmed in the making of this presentation....

Thank you

Have fun learning !!

[email protected]@jr0cketjohn.jr0cket.co.ukclojure.jr0cket.co.uk

Muokkaa otsikon tekstimuotoa napsauttamalla

Muokkaa jsennyksen tekstimuotoa napsauttamallaToinen jsennystasoKolmas jsennystasoNeljs jsennystasoViides jsennystasoKuudes jsennystasoSeitsems jsennystasoKahdeksas jsennystasoYhdekss jsennystaso