Teaching Clojure I

2023 06 01 head

Functional programming is hype for the last ten years. You find a lot of blogs and videos stating object-oriented languages are doomed.

Almost all most popular languages have added some support to functional concepts in the last years.

Java introduced lambda expressions and single abstract method interfaces SAM with Java 8 in March 2014. Java developers are empowered to write functional code for almost ten years.

Other languages such as Python, C++ or C# added their own functional extensions in newer revisions.

Lisp dialects fully supported functional paradigms since the sixties of the last century.

The theory behind functional model is \$lambda\$ calculus. Alonso Church formulated the lambda calculus theory in the thirties of the last century and millennium.

Why are functional programming paradigms suddenly so popular?

Functional languages have the following properties:

  • Functions are first-class objects. That is, functions can be created at runtime, passed around, returned, and in general, used like any other datatype.

  • Data is immutable.

  • Functions are pure; that is, they have no side effects.

OO Object Orientation makes code understandable by encapsulating moving parts.

FP Functional Programming makes code understandable by minimizing moving parts.

— Michael Feathers

For many tasks, functional programs are easier to understand, less error-prone, and much easier to reuse.

Clojure Development Environment

Clojure [1] is a splendid approach to learn functional programming idioms. The language is well-documented.

The integration in the Java ecosystem and virtual machine is well-done.

Purely functional programs typically operate on immutable data.

Instead of altering existing values, altered copies are created and the original is preserved.

Since the unchanged parts of the structure cannot be modified, they can often be shared between the old and new copies, which saves memory.

The IntelliJ plugin Cursive hugely simplifies writing code and testing it [1]. IntelliJ IDEA is probably the most popular IDE for Java developers [2]. People are familiar with its functions and do not need to learn a new tool.

Declarations

You can define values and functions:

(defn square [param] (* param param))                         (1)
(def data (list 1 2 3 4 5))                                   (2)
(def computed (map square data))                              (3)
1 Define defn a square function with one parameter and return the square value of the parameter.
2 Define def a list with five elements and store it the reference named data.
3 Apply the square method on all items of the data list. The computed list is stored in the reference named computed.

Clojure provides extensions to validate type information.

(defn square [^Number param]                                  (1)
  {:pre [(number? param)]}                                    (2)
  (* param param))
1 Define an optional type hint for the parameter with ^Number to state param must be a number.
2 Declare an optional precondition validating parameter values when the function is called.

The equivalent constructs for local value and function declarations inside a function are:

(defn function-1 []
  (let [local-variable-1 local-variable-value-1]              (1)
    body-of-let-block))

(defn function-2 [n]
  (letfn [(do-local [a] (println a))]                         (2)
    (do-local n)))
1 The let expression declares local values in the scope of the function. Use it to minimize global name pollution.
2 The letfn expression declares local functions in the scope of the function. Use it to minimize global name pollution.

Conditional Statements

You still need to write selection code. The usual constructs are:

(defn is-small? [number] (if (< number 100) "yes" "no"))      (1)

(when (= 1 1) (+ 10 1))                                       (2)

(defn pos-neg-or-zero
  "Determines whether or not n is positive, negative, or zero"
  [n]
  (cond                                                       (3)
    (< n 0) "negative"
    (> n 0) "positive"
    :else "zero"))
1 The if expression evaluates a boolean expression. If true returns the first expression, if false returns the optional second expression or nil if not defined.
2 The when expression is an if expression with only the true path.
3 The cond expression evaluates boolean conditions until the first one returning true is found. The associated expression is evaluated and the result returned. If no expression evaluates to true, the else expression is evaluated and returned or nil if not defined.

Some Definitions

Symbols are identifiers that are normally used to refer to something else. They can be used in program forms to refer to function parameters, let bindings, class names, and global vars.

Keywords are symbolic identifiers that evaluate to themselves. They provide very fast equality tests.

(char? (char 97))                                             ;; true
(char? 's)                                                    ;; false

(string? "This is a string")                                  ;; true

(symbol? 'aSymbol)                                            ;; true
(symbol? (symbol "aSymbol"))                                  ;; false

(keyword? :aKeyword)                                          ;; true
(keyword? (keyword "aKeyword"))                               ;; true

Arithmetic

Clojure supports all the number types of the Java language. Here are some examples:

(def aLong 42)
(def aDouble 42.42)
(def aBigInteger 42N)
(def aBigDecimal 42.42M)

(def aRatio 22/7)                                             ;; => 22/7
Operator Description Example

even?

Returns true if n is even

(even? 2) returns true

+

Addition of two operands

(+ 1 2) returns 3

Subtracts second operand from the first

(- 2 1) returns 1

*

Multiplication of both operands

(* 2 2) returns 4

/

Division of numerator by denominator

(float (/ 3 2)) returns 1.5

inc

Increment the value of an operand by 1

(inc 5) returns 6

dec

Decrement the value of an operand by 1

(dec 5) returns 4

max

Returns the largest of its arguments

(max 1 2 3) returns 3

min

Returns the smallest of its arguments

(min 1 2 3) returns 1

quot

Returns the rounded division

(quot 10 3) returns 3

rem

Remainder of division

(rem 3 2) returns 1

Study the documentation, more arithmetic and logical operators are available.

Collections

(def aList (list 1 2 3 4 5))                                  ;; => (1 2 3 4 5)
(def aList '(1 2 3 4 5))                                      ;; => (1 2 3 4 5)
(def aList (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 ()))))))   ;; => (1 2 3 4 5)
(first aList)                                                 ;; => 1
(rest aList)                                                  ;; => (2 3 4 5)

(def aMap {"Fred" 1400, "Bob" 1240})                          ;; => {"Fred" 1400, "Bob" 1240}
(assoc aMap "Sally" 0)                                        ;; => {"Fred" 1400, "Bob" 1240, "Sally" 0}
(dissoc aMap "Fred")                                          ;; => {"Bob" 1240, "Sally" 0}
(get aMap "Bob")                                              ;; => 1240
(aMap "Bob")                                                  ;; => 1240

(def aSet (set [:a :b :c :d]))                                ;; #{:a ;b :c :d}
(def aSet #{:a :b :c :d})                                     ;; #{:a ;b :c :d}
(count aSet)                                                  ;; 4
(aSet :b)                                                     ;; :b

All the Clojure collections are immutable and persistent. The Clojure collections support efficient creation of _modified- versions, by utilizing structural sharing.

The collections are efficient and inherently thread-safe. Collections are represented by abstractions, and there may be one or more concrete realizations.

References

[1] S. D. Halloway, Programming Clojure. 2012 [Online]. Available: https://www.amazon.com/dp/B07BN4C92X


1. The plugin is free upon registration for students, open source developers and personal use. Please buy a license for commercial development. The company shall have a stream of income to continue developing this awesome environment.
2. IDEA has a free community edition. The Ultimate edition is free for students, teachers, and open source developers. Please buy a license for commercial development. The price is very reasonable. The company shall have a stream of income to continue developing this awesome integrated development environment.