I decided to learn a functional language: I chose Clojure, because it runs on the JVM, and it seems more easy to digest for me, having some Java background. In this blog, I am going to write notes mainly for myself. However, since I already take the time to write them down, why not publish them? Maybe they will be useful for someone else too :) Please keep in mind, that I'm an absolute beginner in Clojure. So please use my blog only at your own responsibility. You have been warned ;)
Thursday, August 6, 2015
My first clojure program -- reviewed
A helpful member of StackExchange has reviewed my first clojure program.
Here is an improved version based on his comments.
Tuesday, July 28, 2015
let vs let*
I stumbled upon let* while learning about macros.
Consider the following code:
What does let* mean? Unluckily, it does not seem to be documented anywhere... As a helpful member of StackOverflow explained to me, let* is used to implement let. The addition of let vs. let*, is that let is capable of destructuring the parameters, which let* does not do. Consider the following example:
Consider the following code:
(defmacro somemacro []
(list 'let ['somevar "Value"] 'somevar))
(macroexpand '(somemacro))
Result:
(let* [somevar "Value"] somevar)
What does let* mean? Unluckily, it does not seem to be documented anywhere... As a helpful member of StackOverflow explained to me, let* is used to implement let. The addition of let vs. let*, is that let is capable of destructuring the parameters, which let* does not do. Consider the following example:
(let [[a b c & d :as e] [1 2 3 4 5 6 7]] [a b c d e])Result:
[1 2 3 (4 5 6 7) [1 2 3 4 5 6 7]]
(let* [[a b c & d :as e] [1 2 3 4 5 6 7]] [a b c d e])Result:
CompilerException java.lang.IllegalArgumentException: Bad binding form, expected symbol, got: [a b c & d :as e], compiling:(NO_SOURCE_PATH:1:1)
Monday, July 27, 2015
Remarks on gensym example
Chapter 9 (Writing Macros) of Brave Clojure gives the following example, for the usage of gensym:
One important thing to note is, that the symbol in the macro ('macro-message') in fact does not have to be named differently than the global symbol. It works also with the same name:
On the other hand, just giving a different name to the symbol within the macro won't work:
Finally, in case the symbol is a parameter of the macro, then it is possible to bind it, without gensym:
(def message "Good job!")
(defmacro without-mischief
[& stuff-to-do]
(let [macro-message (gensym 'message)]
`(let [~macro-message "Oh, big deal!"]
~@stuff-to-do
(println "I still need to say: " ~macro-message))))
(without-mischief
(println "Here's how I feel about that thing you did: " message))
Result:
Here's how I feel about that thing you did: Good job! I still need to say: Oh, big deal!
One important thing to note is, that the symbol in the macro ('macro-message') in fact does not have to be named differently than the global symbol. It works also with the same name:
(def message "Good job!")
(defmacro without-mischief
[& stuff-to-do]
(let [message (gensym 'message)]
`(let [~message "Oh, big deal!"]
~@stuff-to-do
(println "I still need to say: " ~message))))
(without-mischief
(println "Here's how I feel about that thing you did: " message))
Result:
Here's how I feel about that thing you did: Good job! I still need to say: Oh, big deal!
On the other hand, just giving a different name to the symbol within the macro won't work:
(def message "Good job!")
(defmacro with-mischief
[& stuff-to-do]
`(let [macro-message "Oh, big deal!"]
~@stuff-to-do))
(with-mischief
(println "Here's how I feel about that thing you did: " message))
Result:
CompilerException java.lang.RuntimeException: Can't let qualified name: user/macro-message, compiling:(NO_SOURCE_PATH:1:1)
Finally, in case the symbol is a parameter of the macro, then it is possible to bind it, without gensym:
defmacro echoit
[message]
(let [message (str "echo: " message)] message))
(echoit "hello")
Result:
"echo: hello"
Operators/special character related to macros
This post is about all special characters which were introduced in the context of macros, in the Brave Clojure Tutorial.
quote '
Causes the first symbol of the list not to be treated as a function call. In other words, it just returns the list as a data structure.
Example without quote:
With quotes:
syntax quote `
Like quoting, but returns fully qualified symbols (i.e. with namespace). Also it allows unquoting a symbol (see below). Example:
unquoting ~
Makes a symbol be evaluated in a syntax-quoted for (syntax-quoted: see above). This is similar to string-interpolation.
Consider the following examples:
With unquoting:
unquote splicing ~@
Like unquoting (see above), but unwraps elements, in case the result is a list. Example:
With unquote:
Automatically generates a new symbol (useful e.g. for let'ting variables within macros). See also gensym. Example for auto-gensym:
quote '
Causes the first symbol of the list not to be treated as a function call. In other words, it just returns the list as a data structure.
Example without quote:
(+ 1 2)Result:
3
With quotes:
'(+ 1 2)Result:
(+ 1 2)
syntax quote `
Like quoting, but returns fully qualified symbols (i.e. with namespace). Also it allows unquoting a symbol (see below). Example:
`(+ 1 1)Result:
(clojure.core/+ 1 1)
unquoting ~
Makes a symbol be evaluated in a syntax-quoted for (syntax-quoted: see above). This is similar to string-interpolation.
Consider the following examples:
`(* 5 (* 2 3))Result:
(clojure.core/* 5 (clojure.core/* 2 3))
With unquoting:
`(* 5 ~(* 2 3))Result:
(clojure.core/* 5 6)
unquote splicing ~@
Like unquoting (see above), but unwraps elements, in case the result is a list. Example:
`(+ ~@(list 1 2 3))Result:
(clojure.core/+ 1 2 3)
With unquote:
`(+ ~(list 1 2 3))Result:
(clojure.core/+ (1 2 3))auto-gensym # (suffix!)
Automatically generates a new symbol (useful e.g. for let'ting variables within macros). See also gensym. Example for auto-gensym:
(defmacro gensym-example [] `(let [name# "Larry Potter"] name#)) (gensym-example)Result:
"Larry Potter"
Useful functions, macros
I'm collecting here the useful functions mentioned in Brave Clojure, Chapter 8 and Chapter 9.
read-string: transforms a string to a data representation
eval: evaluastes a data representation; e.g.:
threading macro ->
Calls the function chain from left to right, i.e. passes the first element as the first parameter of function at second place, then the result of this is the parameter for the function at third place, etc. (c.f. thread-last macro, which does the same, but uses the first value as the last parameter of the next function)
gensym:
Produces unique symbols, with an optional prefix. N.B.: auto-gensym is also possible, with the # suffix.
read-string: transforms a string to a data representation
eval: evaluastes a data representation; e.g.:
(eval (read-string "(+ 1 2 3)"))-->
6macroexpand: shows the data structure returned by a macro; e.g.
(macroexpand '(when true "Hello world"))-->
(if true (do "Hello world"))
threading macro ->
Calls the function chain from left to right, i.e. passes the first element as the first parameter of function at second place, then the result of this is the parameter for the function at third place, etc. (c.f. thread-last macro, which does the same, but uses the first value as the last parameter of the next function)
gensym:
Produces unique symbols, with an optional prefix. N.B.: auto-gensym is also possible, with the # suffix.
Thursday, July 23, 2015
->> (thread-last) macro
I came across ->> macro today (in the example of BraveClojure). This means that it takes a value (the first parameter), and passes it to the list of functions afterwards. More precisely: the value is passed to the first function, whose result is passed to the second, etc.
Consider the following example:
Consider the following example:
(->> [1 2 3 4] (map #(* % 2)) (reduce +))Result:
20
Wednesday, July 22, 2015
My first clojure program
As an exercise, I wrote a number guessing game, i.e. the program thinks of a number (between chosen min. and max. value), which the user has to guess.
I also submitted it for code review, let's see what improvement suggestions do I get :)
NullPointerException when if-branch contains multiple statements
Consider the following code snippet:
The reason is (as described e.g. here), that the return value of (println "Hello") -- which is nil -- will be called as a function, with the return value of the second argument.
This can be prevented by using do:
(if true ((println "Hello") (println "World")) )This will print the following:
Hello World NullPointerException user/eval769 (NO_SOURCE_FILE:1)
The reason is (as described e.g. here), that the return value of (println "Hello") -- which is nil -- will be called as a function, with the return value of the second argument.
This can be prevented by using do:
(if true (do (println "Hello") (println "World")) )Result:
Hello World nil
Monday, July 20, 2015
Implementing own comp function
In Chapter 5 of Clojure for the Brave and True, there is an exercise for implementing an own version of the comp function.
Here is my attempt:
Trying with some examples, it works the same way as the built-in one :)
There is one special case, where my implementation behaves differently from the built-in one, and that is when there are zero functions:
Some final remarks:
Here is my attempt:
Trying with some examples, it works the same way as the built-in one :)
((comp + - -) 1 2 3 4 5)Result:
13
((my-comp + - -) 1 2 3 4 5)Result:
13
((comp clojure.string/upper-case
clojure.string/lower-case
(fn [somestr] (str " " somestr " "))
clojure.string/trim)
"heLLo")
Result:
" HELLO "
((my-comp clojure.string/upper-case
clojure.string/lower-case
(fn [somestr] (str " " somestr " "))
clojure.string/trim)
"heLLo")
Result:
" HELLO "
There is one special case, where my implementation behaves differently from the built-in one, and that is when there are zero functions:
((comp) 1 2 3)Result:
ArityException Wrong number of args (3) passed to: core/identity clojure.lang.AFn.throwArity (AFn.java:429)
((my-comp) 1 2 3)Result:
NullPointerException clojure.core/apply (core.clj:624)So both versions leak some implementation details in this case, and cause an error...
Some final remarks:
- the built-in version interprets the last function first; that's why I had to reverse the parameters
- I encountered a problem, while processing the arguments: for the first function, they are in a form of a sequence, and can thus be applied; for all other functions, they are just raw values, and thus it is necessary to call the function directly (without apply)
Sunday, July 19, 2015
Finished Chapter 4 of Clojure for the Brave and True
I have just finished the tasks at the end of Chapter 4 "Core Functions in Depth".
See my solutions below (hosted on github)
Some important lessons learned:
See my solutions below (hosted on github)
Some important lessons learned:
- When inserting into a sequence, make sure that the new sequence to insert is of the right type. Consider the following code:
(into [{:name "Edward Cullen" :glitter-index 10}{:name "Bella Swan" :glitter-index 0} {:name "Charlie Swan" :glitter-index 0}] {:name "Mr Bean" :glitter-index 5})
Result:
[{:name "Edward Cullen", :glitter-index 10}{:name "Bella Swan", :glitter-index 0} {:name "Charlie Swan", :glitter-index 0}[:name "Mr Bean"] [:glitter-index 5]]
Since in this case, the new sequence is a map, each key-value pair is treated as a new vector.
The right way to insert a new map entry is the following:
(into [{:name "Edward Cullen" :glitter-index 10}{:name "Bella Swan" :glitter-index 0} {:name "Charlie Swan" glitter-index 0}] [{:name "Mr Bean" :glitter-index 5}])
Result:
[{:name "Edward Cullen", :glitter-index 10}{:name "Bella Swan", :glitter-index 0} {:name "Charlie Swan", :glitter-index 0}{:name "Mr Bean", :glitter-index 5}]
- and cannot be applied. There are some other ways to achieve the same result.
One possible solution is the following:
(apply = true '(true false true))
Understanding "Continuation Passing Style" Recursive function for Factorial
Another very good source of Clojure knowledge is the book The Joy of Clojure, from where the following example is taken:
Btw., for the meaning of "Continuation Passing Style", see the book, or Wikipedia. This post is only about understanding how this code sample works, without worrying too much about the theoretical background ;)
My first thought was: how can this even work? When we call cont for the last time, then n is zero, and thus the whole product will be zero. (Why did I think this? Because, we check if n is zero, and if so, we call cont with 1. So n=0 and v=1. At least that's what I thought at the beginning.)
However, trying out the function demonstrates the opposite:
So let's add some debug code, to understand what is going on:
Invoking again the function in the same way as above, yields the following result:
So we can see, that in the last step, n is 1. But how is that possible? We just said that it was zero.
Well, the reason is, that when (k 1) is called, then k is the cont instance from the previous call of fac-pos (where n=1). I.e., in the closure of that instance of cont, n=1. And v=1 parameter is the one we just passed to it. And at this point, k is the cont instance from n=2. Thus we go back through the whole chain, and get the desired result at the end.
(defn fac-cps [n k]
(letfn [(cont [v] (k (* v n)))] ;; #1_fac-cps: Next continuation
(if (zero? n) ;; #2_fac-cps: Accept continuation
(k 1) ;; #3_fac-cps: Return continuation
(recur (dec n) cont))))
Btw., for the meaning of "Continuation Passing Style", see the book, or Wikipedia. This post is only about understanding how this code sample works, without worrying too much about the theoretical background ;)
My first thought was: how can this even work? When we call cont for the last time, then n is zero, and thus the whole product will be zero. (Why did I think this? Because, we check if n is zero, and if so, we call cont with 1. So n=0 and v=1. At least that's what I thought at the beginning.)
However, trying out the function demonstrates the opposite:
(fac-cps 5 identity)will return 120.
So let's add some debug code, to understand what is going on:
(defn fac-cps [n k]
(letfn [(cont [v]
(do
(println (str "v=" v "n=" n))
(k (* v n)))
)
]
(if (zero? n)
(k 1)
(recur (dec n) cont))))
Invoking again the function in the same way as above, yields the following result:
v=1n=1 v=1n=2 v=2n=3 v=6n=4 v=24n=5
So we can see, that in the last step, n is 1. But how is that possible? We just said that it was zero.
Well, the reason is, that when (k 1) is called, then k is the cont instance from the previous call of fac-pos (where n=1). I.e., in the closure of that instance of cont, n=1. And v=1 parameter is the one we just passed to it. And at this point, k is the cont instance from n=2. Thus we go back through the whole chain, and get the desired result at the end.
Why reverse in my-partial
I'm currently going through the site Clojure for the Brave and True, which I find an excellent resource for getting acquainted with the language.
Chapter 4 describes a possible implementation of the partial function:
The question I asked myself was: why do we need to reverse the args?
Let's examine that!
First we create a function, to which we will apply my-partial later:
Now, let's apply my-partial to test1:
We get the expected output, i.e.:
The key is the 'into' function call:
When we try to apply the list to the test1 function, we get the expected result:
Note, that vectors follow a different order when using into:
Indeed, when using a vector, the parameters are not passed in the expected order.
The different behavior is reported in an example of the documentation of into:
The reason for this difference is explained in a stack-overlow post. According to the linked answers, lists in clojure are implemented as linked-lists, thus it is more efficient to add new elements at their beginning. In contrast, for vectors it is easier to add an element at the end: in this case, there is no need to shift the elements of the vector. In fact, if extra empty space was already reserved, the addition of a new element becomes very cheap.
Chapter 4 describes a possible implementation of the partial function:
(defn my-partial [partialized-fn & args] (fn [& more-args] (apply partialized-fn (into more-args (reverse args)))))
The question I asked myself was: why do we need to reverse the args?
Let's examine that!
First we create a function, to which we will apply my-partial later:
(defn test1 [arg1 arg2 arg3 arg4] (println (str "arg1='" arg1 "' arg2='" arg2 "' arg3='" arg3 "' arg4='" arg4 "'")))
Now, let's apply my-partial to test1:
(def test1-partial (my-partial test1 "arg1" "arg2")) (test1-partial "arg3" "arg4")
We get the expected output, i.e.:
arg1='arg1' arg2='arg2' arg3='arg3' arg4='arg4' nil
The key is the 'into' function call:
(into '("arg3" "arg4") (reverse '("arg1" "arg2")))
Which returns:
("arg1" "arg2" "arg3" "arg4")
When we try to apply the list to the test1 function, we get the expected result:
(apply test1 (into '("arg3" "arg4") (reverse '("arg1" "arg2"))))
With the output:
arg1='arg1' arg2='arg2' arg3='arg3' arg4='arg4' nil
Note, that vectors follow a different order when using into:
(into ["arg3" "arg4"] (reverse ["arg1" "arg2"]))Result:
["arg3" "arg4" "arg2" "arg1"]
Indeed, when using a vector, the parameters are not passed in the expected order.
(apply test1 (into ["arg3" "arg4"] (reverse ["arg1" "arg2"])))Result:
arg1='arg3' arg2='arg4' arg3='arg2' arg4='arg1' nil
The different behavior is reported in an example of the documentation of into:
; Items are conj'ed one at a time, which puts them at the head of ; the destination list user=> (into () '(1 2 3)) (3 2 1) ; This does not happen for a vector, however, due to the behavior of conj: user=> (into [1 2 3] '(4 5 6)) [1 2 3 4 5 6]
The reason for this difference is explained in a stack-overlow post. According to the linked answers, lists in clojure are implemented as linked-lists, thus it is more efficient to add new elements at their beginning. In contrast, for vectors it is easier to add an element at the end: in this case, there is no need to shift the elements of the vector. In fact, if extra empty space was already reserved, the addition of a new element becomes very cheap.
Subscribe to:
Comments (Atom)