Book Image

Clojure Programming Cookbook

Book Image

Clojure Programming Cookbook

Overview of this book

When it comes to learning and using a new language you need an effective guide to be by your side when things get rough. For Clojure developers, these recipes have everything you need to take on everything this language offers. This book is divided into three high impact sections. The first section gives you an introduction to live programming and best practices. We show you how to interact with your connections by manipulating, transforming, and merging collections. You’ll learn how to work with macros, protocols, multi-methods, and transducers. We’ll also teach you how to work with languages such as Java, and Scala. The next section deals with intermediate-level content and enhances your Clojure skills, here we’ll teach you concurrency programming with Clojure for high performance. We will provide you with advanced best practices, tips on Clojure programming, and show you how to work with Clojure while developing applications. In the final section you will learn how to test, deploy and analyze websocket behavior when your app is deployed in the cloud. Finally, we will take you through DevOps. Developing with Clojure has never been easier with these recipes by your side!
Table of Contents (16 chapters)
Clojure Programming Cookbook
Credits
About the Authors
About the Reviewer
www.PacktPub.com
Preface

Using and defining functions


In this recipe, we will review Clojure's function definitions:

  • Defining simple functions

  • Defining variadic functions

  • Defining multiple arity functions

  • Defining functions that specify arguments using a keyword

  • Defining functions with a pre-condition and a post-condition

Getting ready

You only need REPL, as described in the first recipe in this chapter, and no additional libraries. Start REPL so that you can test the sample code immediately in this recipe.

How to do it...

Here, we will learn how to define functions using Clojure. Let's start with a simple function which returns Hello world:

Defining simple functions

Let's start with a minimum function definition. Here is a minimal syntax of defn:

(defn funtion-name [arg1 arg2 ...] 
  expr-1  
  expr-2 
  .. 
  expr-n 
  ) 

defn is a special form. The first argument is a function name and is followed by a vector of one or more arguments, then one or more expressions. The last expression is returned to the caller.

Here, we define a very simple function. The hello function returns a Hello world string:

(defn hello [s] 
  (str "Hello world " s " !")) 
;;=> #'living-clojure.core/hello 
(hello "Nico") 
;;=> "Hello world Nico !" 
(hello "Makoto") 
;;=> "Hello world Makoto !" 

The next sample defines a simple adder function:

(defn simple-adder [x y] 
  (+ x y) 
  ) 
;;=> #'living-clojure.core/simple-adder 
(simple-adder  2 3) 
;;=> 5 

Defining variadic functions

A variadic function allows a variable number of arguments. The next example defines another adder. It may have an arbitrary number of arguments:

(defn advanced-adder [x & rest] 
  (apply + (conj rest x)) 
  ) 
;;=> #'living-clojure.core/advanced-adder 
(advanced-adder 1 2 3 4 5) 
;;=> 15 

Defining multiple arity functions

Here, we will introduce the multiple arity function. The following function defines a single argument function and a couple of argument functions with the same defn:

(defn multi-arity-hello 
  ([] (hello "you")) 
  ([name] (str "Hello World " name " !"))) 
;;=> #'living-clojure.core/multi-arty-hello 
(multi-arity-hello) 
;;=> Hello World you ! 
(multi-arity-hello "Nico") 
  ;;=> Hello World Nico ! 

Defining functions that specify arguments using a keyword

Sometimes, specifying a keyword is useful, since it is not necessary to remember the order of arguments.

The next example shows how to define such a function. The options are :product-name, :price, and :description. The :or expression supplies default values if any values in keys are omitted:

(defn make-product-1 
  [serial & 
   {:keys [product-name price description] 
    :or {product-name "" price nil description "no description !"} 
    }  
   ] 
   {:serial-no serial :product-name product-name 
    :price price :description description} 
  ) 
;;=> #'living-clojure.core/make-product-1 
 
(defn make-product-2 
  [serial & 
   {:keys [product-name price description] 
    :or {:product-name "" :description "no description !"} 
    }    
   ] 
   {:serial-no serial :product-name product-name 
    :price price :description description} 
  ) 
;;=> #'living-clojure.core/make-product-2 
 
(make-product-1 "0000-0011") 
;;=> {:serial-no "0000-0011", :product-name "", :price nil, :description "no description !"} 
(make-product-2 "0000-0011") 
;;=> {:serial-no "0000-0011", :product-name nil, :price nil, :description nil} 

Defining functions with pre-condition and post-condition

Clojure can define functions with pre-condition and post-condition. In the following defn, :pre checks whether an argument is positive. :post checks whether the result is smaller than 10:

(require '[clojure.math.numeric-tower :as math]) 
;;=> nil 
(math/sqrt -10) 
;;=> NaN 
(defn pre-and-post-sqrt [x] 
  {:pre  [(pos? x)] 
   :post [(< % 10)]} 
   (math/sqrt x)) 
;;=> #'living-clojure.core/pre-and-post-sqrt 
(pre-and-post-sqrt 10) 
;;=> 3.1622776601683795 
(pre-and-post-sqrt -10) 
;;=> AssertionError Assert failed: (pos? x)  user/pre-and-post-sqrt (form-init2377591389478394456.clj:1) 
(pre-and-post-sqrt 120) 
AssertionError Assert failed: (< % 10)  user/pre-and-post-sqrt (form-init2377591389478394456.clj:1) 

Moreover, in this recipe, we will show a more complicated function. The make-triangle function prints a triangle with a character. If this function is called without a :char argument, it prints a triangle made of asterisks. If it is called with a :char argument, it prints a triangle comprising characters specified by :char:

(defn make-triangle 
  [no & {:keys [char] :or {char "*"}}] 
  (loop [x 1] 
    (when (<= x no)  
      (dotimes  
          [n (- no x)] (print " ")) 
      (dotimes  
          [n  
           (if (= x 1)  
             1     
             (dec (* x 2)))] 
        (print char)) 
      (print "\n") 
      (recur (inc x)) 
      ) 
    ) 
  ) 
(make-triangle 5)     
;;=>     * 
;;=>    *** 
;;=>   ***** 
;;=>  ******* 
;;=> ********* 
;;=> nil 
(make-triangle 6 :char "x"))     
;;=>      x 
;;=>     xxx 
;;=>    xxxxx 
;;=>   xxxxxxx 
;;=>  xxxxxxxxx 
;;=> xxxxxxxxxxx 

How it works...

We have already reviewed how to define functions and how to use them. You should understand how they work after reviewing the previous section.

To define functions using defn is the same as vars bind to functions by fn as follows:

(defn pow-py-defn [x] (* x x)) 
;;=> #'living-clojure/pow-py-defn 
(def pow-by-def (fn [x] (* x x))) 
;;=> #'living-clojure/pow-by-def 
(pow-py-defn 10) 
;;=> 100 
(pow-by-def 10) 
;;=> 100 

There's more...

clojure.repl has some useful functions to use with other functions. To get a symbol in the specific namespace, use clojure.repl/dir:

(require 'clojure.string) 
;;=> nil 
(clojure.repl/dir clojure.string) 
;;=> blank? 
;;=> capitalize 
;;=> escape 
;;=> join 
;;=> lower-case 
;;=> re-quote-replacement 
;;=> replace 
;;=> replace-first 
;;=> reverse 
;;=> split 
;;=> split-lines 
;;=> trim 
;;=> trim-newline 
;;=> triml 
;;=> trimr 
;;=> upper-case 
;;=> nil 

To get the documentation of a function, use clojure.repl/doc:

(clojure.repl/doc clojure.string/trim) 
------------------------- 
;;=> clojure.string/trim 
;;=> ([s]) 
;;=>   Removes whitespace from both ends of string. 
;;=> nil 

To get symbols that have a specific string, use clojure.repl/apropos:

(clojure.repl/apropos "defn") 
;;=> (clojure.core/defn 
;;=>  clojure.core/defn- 
;;=>  deps.compliment.v0v2v4.compliment.sources.local-bindings/defn-like-forms 
;;=>  deps.compliment.v0v2v4.deps.defprecated.v0v1v2.defprecated.core/defn)