Upgrade to Pro — share decks privately, control downloads, hide ads and more …

0 to 35 mph with Clojure

0 to 35 mph with Clojure

An introduction to Clojure that is intended for audiences who are new to Lisp and functional programming

Travis Douce

March 08, 2017
Tweet

More Decks by Travis Douce

Other Decks in Programming

Transcript

  1. Outline • Clojure’s Guiding Principles ◦ Simplicity • What is

    Clojure? • Clojure syntax • Clojure examples • Code is data
  2. Most languages Clojure It Looks Different • When people come

    to Clojure the first they do is cringe ◦ We all need just a little understanding and empathy ◦ Yes, it looks different than languages you are accustomed to working in
  3. Infix It Looks Different Prefix • Infix Notation ◦ Most

    languages use arithmetical operations ◦ Where it is operators between operands • Prefix Notation ◦ Prefix notation is operators followed by operands
  4. It Looks Different Infix Prefix (f arg1 arg2 arg3) •

    Infix ◦ Operand operator operand operator • Prefix ◦ Operator operand operand
  5. • As software developers, we want to be able to

    build complex things • Requirements of our software are becoming increasingly demanding and complicated • This castle is complicated and intricate ◦ We, as software developers, want to build complex structures like these ◦ It has turrets, windows, roofs, internal rooms, a dungeon (which you can’t see) ◦ It even has a witch • If you look closely, you’ll see that this castle is knitted together • What happens if ◦ We want to replace the window ◦ Replace the roof ◦ Add another wing • This becomes difficult with this approach. ◦ It is a complex weaved pattern ◦ It’s intertwined ◦ Adding a new wing necessitates tearing a hole in the side of castle and mending it in ▪ This would be difficult
  6. • Unlike the knitted castle, this castle is made of

    logos • Legos are small composable pieces • Pieces that are not intertwined and bound the neighboring pieces • We can snap them together into an infinite number of shapes • If we need to ◦ Replace the roof ◦ The window ◦ Add a wing • If we need to pivot quickly, we could then make a resort instead ◦ It already has a pool • We can do this more easily do this with legos than the knitted castle • I would say that the lego pieces are simple in their nature ◦ They do one thing well, they snap/connect to another piece and you can easily disconnect them
  7. Simple Made Easy • Simple is not Easy • Just

    because it’s easy doesn’t mean it’s simple • Simplicity ◦ Prerequisite for reliability ◦ Ease of understanding ◦ Ease of change ◦ Ease of debugging • Rich Hickey, the creator of Clojure gave a talk “Simple Made Easy” ◦ He spoke about how software should be simple, but not necessarily easy • Simple Made Easy ◦ Simple is often erroneously mistaken for easy. i. "Easy" means "to be at hand", "to be approachable" ◦ “Complex” means "being intertwined", "being tied together" ◦ "Simple" is the opposite of "complex" ◦ Simple != easy. ◦ Often times simple is hard ◦ Easy is overrated in the software industry. i. "Can I get this thing running in 5 seconds?" ii. Npm install everything iii. It does not matter if the result is a large ball of mud. iv. All it matters is to be done quickly.
  8. • Simplicity matters in software ◦ We should ask ourselves

    these questions: i. Does the software do what is supposed to do? ii. Is it of high quality? iii. Can we rely on it? iv. Can problems be fixed along the way? v. Can requirements change over time? ◦ Complexity in software can be i. Interleaving, Entwining, Braiding responsibilities ◦ Can I figure out what the system is doing and make changes? i. If it is a large system made of a million simple composable parts 1. “yes” it is more approached, maybe not simple, 2. You can make changes to the system 3. Add functionality ii. If is a large system made of complex things, then “no” it is more difficult to understand 1. Takes longer to comprehend systems 2. Takes longer to make changes to the system • This is all true about Software Development. • Rich Hickey applied these concepts to Clojure when he was developing it • He really intended Clojure to be “Simple made easy”
  9. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM
  10. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM
  11. Lisp • Oldest high-level programming (after Fortran) • Roots in

    Artificial Intelligence • Code as data ◦ Homoiconic ◦ Rich macro system • Other Lisps ◦ Common Lisp, Racket, Scheme • Parentheses • Oldest high-level programming (after Fortran) ◦ Strong abstractions that remove details about the computer ▪ Such as memory management ▪ Python, Ruby, PHP vs C, Assembly • Roots in Artificial Intelligence • Code as data ◦ Homoiconic ▪ Express programs in a programming language’s primitive data types • When the reader (parser) reads a source file, the ABS built looks exactly like the source • When you can access a piece of code as data, you can analyze and modify it just like any other data • Your programming language doesn’t need to know this data represents code. ▪ In most languages the AST’s data structure is inaccessible within the programming language • The programming language space and the compiler space are forever separated ◦ We’ll cover this later in more detail
  12. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM
  13. Dynamic Language • Static vs Dynamic • Manipulate program at

    runtime ◦ Adding new code ◦ Extending objects and definitions ◦ Modifying the type system • Many of these were first implemented Lisp • Static vs Dynamic ◦ The debate between static and dynamic language is not black and white ◦ Haskell vs Clojure ▪ Haskell • Powerful type system that is checked at compile time • If your program compiles it is free from common and not so common errors. ◦ Prevents you from making silly mistakes ◦ But also may prevent you from doing some things that may be viewed as valid ▪ Clojure • Short compilation step (basically just reading syntax) • Practically no limits as to what can be done at runtime • Won't prevent me from silly mistakes. • Manipulate program at runtime ◦ Metaprogramming ◦ Macros ◦ Closures
  14. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM
  15. Functional • Mostly functional ◦ Philosophy that functional programs are

    more robust • Functions are first class citizens • Immutable ◦ State is a main source of complexity, and hence bugs ◦ Can it be done in languages that support mutability? ◦ Implications for concurrent programming • Mostly functional ◦ Provides the tools to avoid mutable state ◦ Clojure is impure ▪ Doesn’t force your program to be referentially transparent • Given a function and an input value, you will always receive the same output
  16. Functional • Mostly functional ◦ Philosophy that functional programs are

    more robust • Functions are first class citizens • Immutable ◦ State is a main source of complexity, and hence bugs ◦ Can it be done in languages that support mutability? ◦ Implications for concurrent programming • Functions are first class citizens ◦ You can pass functions as arguments ◦ Ruby, Python, Javascript support this •
  17. Functions are First Class Citizens 1) Function name “signature” that

    takes a function named “signoff” as an argument 2) Pass function as argument to “signature” • Functions are first class citizens ◦ You can pass functions as arguments ◦ Ruby, Python, Javascript support this
  18. Functional • Mostly functional ◦ Philosophy that functional programs are

    more robust • Functions are first class citizens • Immutable ◦ State is a main source of complexity, and hence bugs ◦ Can it be done in languages that support mutability? ◦ Implications for concurrent programming • Immutable ◦ After switching to an immutable language, I have found that state is the main source of complexity, and hence bugs ◦ Difficult concept to convey to folks who work in languages that support mutablity ◦ I can say that overtime I have experienced fewer bugs. ◦ It’s as if an entire class of bugs ceased to exist ◦ Can it be done in languages that support mutability? ▪ Yes, but with great effort ▪ The easiest way to avoid mutating state is to use immutable data structures. ▪ Clojure provides a set of immutable data structures • lists vectors, sets and maps. ▪ Since they can’t be changed, 'adding' or 'removing' something from an immutable collection • It means creating a new collection just like the old one but with the needed change • New versions can share structure with the prior version to maintain performance ◦ I.e. it is not a naive copy ▪ I find that is one of the difficulties/impedances for folks
  19. ▪ coming from mutable languages • Ironically, this is what

    makes the functional programs easier to reason about
  20. Immutable 1) Assign “name” to “Travis” 2) Reassign “name” to

    “Jonathan” • Mostly functional ◦ Provides the tools to avoid mutable state ◦ Functions as first-class objects ◦ Emphasizes recursive iteration instead of side-effect based looping ◦ Clojure is impure ▪ Doesn’t force your program to be referentially transparent • Given a function and an input value, you will always receive the same output • Functions are first class citizens ◦ You can pass functions as arguments ◦ Ruby, Python, Javascript support this • Immutable ◦ After switching to an immutable language, I have found that state is the main source of complexity, and hence bugs ◦ I find that this is a difficult concept to convey to folks who work in languages that support mutablity ◦ I can say that overtime I have experienced fewer bugs. ◦ It’s as if an entire class of bugs ceased to exist ◦ Can it be done in languages that support mutability? ▪ Yes, but with great effort ▪ The easiest way to avoid mutating state is to use immutable data structures.
  21. ▪ Clojure provides a set of immutable data structures •

    lists vectors, sets and maps. ▪ Since they can’t be changed, 'adding' or 'removing' something from an immutable collection • It means creating a new collection just like the old one but with the needed change • New versions can share structure with the prior version to maintain performance ◦ I.e. it is not a naive copy ▪ I find that is one of the difficulties/impledences for folks coming from mutable languages • Ironically, this is what makes the functional programs easier to reason about
  22. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM
  23. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM •
  24. Concurrent Programming • Multi-threaded • Immutable data structures can be

    shared across threads • Allows state change ◦ Software Transactional Memory System ◦ Ensure consistency and reliability ◦ No need to manage locks, and etc. • Today’s applications are being asked to do more and more ◦ Manage more data and faster • Multi-threaded • Immutable data structures can be shared across threads ◦ All of clojure core data structures are immutable ◦ This is a benefit of functional programming and immutability • Allows state change ◦ Multi-threaded applications can help address these requests ▪ Core data structures are immutable and can be shared readily between threads ▪ Provides mechanism to ensure that when state changes it remains consistent and reliable ▪ This is done with STM
  25. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM •
  26. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM •
  27. Polymorphism • Make programs easier to change and extend •

    Presenting the same interface for differing data types. • This achieved in languages like Ruby with “Duck Typing”
  28. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM •
  29. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM •
  30. Hosted on the JVM • A hosted language • Compiles

    all functions to JVM bytecode • Access to Java’s rich ecosystem ◦ Java Interop
  31. What is Clojure? • Lisp • Dynamic Language • Functional

    • Concurrent Programming • Polymorphic • Hosted on the JVM •
  32. Benefits I’ve Experienced • About productive as I was with

    Ruby ◦ Ruby - 3’ish years ◦ Clojure - 1 year • Focus on domain problem rather than language • Productivity gains on server (relative to Ruby) • Huge productivity gains on client (relative to JavaScript) • Attribute to ◦ Being a more experienced developer ◦ Less magic in Clojure (than Ruby) ▪ What’s happening in an application is more transparent ◦ Minimal syntax ▪ Apart from parenthesis ▪ Managed by ide/text-editor ◦ Immutability ▪ Again, I found that a lot of complexity derives from maintaining state ▪ The less amount of state you manage, the less complexity ▪ This is provided to you b/c data structures are immutable • It makes it difficult to maintain state • State that is maintained ◦ Has a very explicit scope and small surface area ◦ Or is pushed outside the application all together ▪ The db ◦ Language simplicity/terseness ▪ Less noise ▪ No class constructors
  33. ▪ Semi-colons ▪ Etc ◦ Most domain concepts/objects are just

    maps ▪ not represented as classes all with their special methods ▪ This is equivalent to all class responding to the same methods ◦ REPL
  34. ClojureScript is a THING! • Yes, you can write ClojureScript

    • It compiles to JavaScript • Implements the same’ish interface as Clojure ◦ So you get a lot of the same benefits ◦ Apart from macros and concurrency • No more strange JS syntax. • ClojureScript is predictable and very nice to work with • The JavaScript community is moving towards functional programming ◦ Immutable.js ◦ Ramda ◦ Lodash • I say that the JavaScript community is being pushed by the functional programming • There are so many aspects that ClojureScript simplifies ◦ No npm, yarn, and whatever next month is ◦ No special libraries for immutable data structures ◦ No special libraries for functional programming functions ◦ It’s is just given to you by ClojureScript
  35. • This “def” is defined within a namespace • We

    “def” “friends” to a vector containing the string “Jonathan”
  36. • “Let” is a clojure special form • Create a

    “let” binding ◦ Binding the vector “friends” to [“No-one”] ◦ “Ben” is then “conj”ed onto the vector “friends” • The important thing to note is that “friends” in the “let” block is scoped to the “let” block • Outside of the “let” block, “friends” is [“Jonathan”]
  37. • “Let” is a clojure special form • Create a

    “let” binding ◦ Binding the vector “friends” to [“No-one”] ◦ “Ben” is then “conj”ed onto the vector “friends” •
  38. • The important thing to note is that “friends” in

    the “let” block is scoped to the “let” block • Outside of the “let” block, “friends” is [“Jonathan”]
  39. • This “def” is defined within a namespace • We

    “def” “friends” to a vector containing the string “Jonathan”
  40. • We create a function named “add-friend” • The function

    “add-friend” ◦ Takes one argument (name “friend”) ◦ We add the “friend” to the vector “friends”
  41. • A function called “shout-at-friends” that takes a collection of

    friends • Uses the function “map” to iterate over each item in the collection and applies the function “str/upper-case” to each element
  42. • We provide the function “add-friend” to “shout-at-friends” • “add-friend”

    is evaluated before “shout-at-friends” is evaluated. • “Add-friend” returns a collection “[“jonathan”, “ben”]”
  43. • Really we want to yell at our friends •

    We define a function “yell” that takes a collection of friends • It “map”s over each item in the collection and applies an anonymous function to each element in the collection ◦ The anonymous function takes one argument and adds “!” to each argument
  44. • Now we are yelling at our friends! • Exactly

    what we want • But it is a bit messy ◦ We get this deeply nested function which can be difficult to reason about
  45. • This is a much nicer way to accomplish yelling

    at our friends • The “->” is a macro • It is the “thread-first” macro • The first argument is a collection ◦ The return value of each function is provided at the first argument to the next function
  46. • What code is being evaluated? • We can use

    a special form that Clojure provides called a “macroexpand” ◦ “Macroexpand” returns a “macro”s expansion ◦ Meaning it returns the data structure that will be evaluated • The return value looks exactly like the deeply nested function we wrote previously ◦ Except that each function is prefixed with the namespace ◦ In this case, the namespace is “user”, the default clojure namespace • The macro was able to manipulate the program at runtime
  47. Code as Data • Clojure is homoiconic ◦ Express programs

    in a programming language’s primitive data types ◦ Your code is data and data is code • Code as data ◦ Homoiconic ▪ Express programs in a programming language’s primitive data types • When the reader (parser) reads a source file, the ABS built looks exactly like the source • When you can access a piece of code as data, you can analyze and modify it just like any other data • Your programming language doesn’t need to know this data represents code. ▪ In most languages the AST’s data structure is inaccessible within the programming language • The programming language space and the compiler space are forever separated
  48. Reader “(+ 1 2 3)” (+ 1 2 3) Abstract

    Syntax Tree Evaluator 6 Accessible To You! This is where Macros live
  49. • The “read-string”, the “Reader portion” from the previous slide

    is available to use a function • The string “(+ 1 2 3)” is passed to the “read-string” function • It returns an unevaluated clojure list ◦ Which is data that represents our code!
  50. • The “eval” function is the “Evaluator” from the previous

    slide • It takes the clojure list (that represents our code) and evaluates the expression and returns 6! • Macros live between the reader and evaluator steps
  51. • This is an “if” expression • If predicate returns

    true (which “true” does) ◦ Then “My best friend!” is returned ◦ Otherwise “I’ve lost you forever :/” • In this case, “My best friend!” is returned
  52. • There is also the “when” macro • It returns

    ◦ the value supplied when the predicate evaluates to “true” ◦ Otherwise it returns “nil” • In this case “when” returns “My best friend!”
  53. • In this case “when” returns “nil” because “false” does

    not evaluate to “true” • “When” is a macro • Remember “macro”s live between the “reader” and “evaluator” and is accessible to clojure ◦ Clojure can manipulate the data structures returned from the “reader” before it is sent to the “evaluator”
  54. • We can use a special form that Clojure provides

    called a “macroexpand” ◦ “Macroexpand” returns a “macro”s expansion ◦ Meaning it returns the data structure that will be evaluated • In this case, we see that “when” is just syntactic sugar for an “if” expression
  55. Outline • Clojure’s Guiding Principles ◦ Simplicity • What is

    Clojure? • Clojure syntax • Clojure examples • Code is data
  56. References • https://clojure.org/ • http://www.braveclojure.com/ • http://blog.venanti.us/why-clojure/ • https://en.wikipedia.org/wiki/Lisp_(programming_language) •

    https://en.wikipedia.org/wiki/Abstract_syntax_tree • http://blog.muhuk.com/2014/09/28/is_clojure_homoiconic.html#.WL7HsxLyvU I • http://stackoverflow.com/a/4938609/222185 • https://en.wikipedia.org/wiki/Dynamic_programming_language • https://robots.thoughtbot.com/back-to-basics-polymorphism-and-ruby