I'm looking at the trie implementation in Clojure here:
http://merrigrove.blogspot.co.uk/2010/07/clojure-implementation-of-dictionary.html
I'm struggling with the query-node-with-path function. How would I call it to return all child words of a stem?
I'm sure the secret is in the function call with the arguments
[ [ s & _ :as source ]
{type :type children :children wf :word-fragment :as node}
acc]
But I can't figure it out.
Looking at the rest of the code, I think query-node is the function you want, it calls query-node-with-path, and only returns the part you want. Its two args expand to source and node (it's just defined poorly such that you don't know what its args are unless you look at the function it calls).
To actually explain the args to query-node-with-path
That is a destructured argument list, describing three arguments:
source, which is a sequence, and the first element is bound to s
node, which is a hash-map, with keys type, children, and word-fragment bound to type, children and wf
acc (which idiomatically means an accumulator argument)
It is clear that acc is added to as the function recurs, and it is an optional argument, since the function has another arity that creates acc as [] and calls the full arity version.
(As an aside, the code there is very inconsistently formatted, and harder to read than it should be)
Related
Just when I thought I had a pretty good handle on macros, I came across the source for some which looked a bit odd to me at first glance.
(defn some
[pred coll]
(when (seq coll)
(or (pred (first coll)) (recur pred (next coll)))))
My first instinct was that seems like it would be stack consuming, but then I remembered: "No, dummy, or is a macro so it would simply expand into a ton of nested ifs".
However mulling it over a bit more I ended up thinking myself in a corner. At expansion time the function source would look like this:
(defn some
[pred coll]
(when (seq coll)
(let [or__4469__auto__ (pred (first coll))]
(if or__4469__auto__
or__4469__auto__
(recur pred (next coll))))))
Now what's got me confused is that final recur call. I've always thought that macroexpansion occurs prior to runtime, yet here you have to actually call the already expanded code at runtime in order for the second macroexp .... wait a second, I think i just figured it out.
There is no second macroexpansion, there are no nested if blocks, only the one if block. The call to recur just keeps rebinding pred and coll but the same single block above keeps testing for truth until it finds it, or the collection runs out and nil is returned.
Can someone confirm if this is a correct interpretation? I had initially confused myself thinking that there would be an interleaving of macroexpansion and runtime wherein at runtime the call to recur would somehow result in a new macro call, which didn't make sense since macroexpansion must occur prior to runtime. Now I think I see where my confusion was, there is only ever one macro expansion and the resulting code is used over and over in a loop.
To start with, note that any function can serve as an implicit loop expression. Also, recur works just like a recursive function call, except it does not use up the stack because of a compiler trick (that is why loop & recur are "special forms" - they don't follow the rules of normal functions).
Also, remember that when is a macro that expands into an if expression.
Having said all that, you did reach the correct conclusion.
There are two modes of recursion going on here:
The or macro is implicitly recursive, provoked by the sequence of argument
forms into generating a tree of if forms.
The some function is explicitly recursive, provoked into telling the single
sequence of its final argument. The fact that this recursion is
recurable is irrelevant.
Every argument to the or macro beyond the first generates a nested if form. For example, ...
=> (clojure.walk/macroexpand-all '(or a b c))
(let* [or__5501__auto__ a]
(if or__5501__auto__ or__5501__auto__
(let* [or__5501__auto__ b]
(if or__5501__auto__ or__5501__auto__ c))))
You have two arguments to or, so one if form. As Alan Thompson's excellent answer points out, the surrounding when unwraps into another if form.
You can have as many nested if forms as you like, the leaves of the if tree, all of them, are in tail position. Hence all immediate recursive calls there are recurable. If there was no such tail recursion, the recur call would fail to compile.
E. g. why can reduce not be used with multiple input sequences as its arguments like map, mapv?
What are the implementors/Rich Hickeys reasons behind that decision?
Would it affect performance, if so how? Is it simply language design that wants to lead us in the "right" direction, if so why?
EDIT: As found out in the discussion (with the help of #Alex), here are the overloads of a variadic reduce
([f coll])
([f val & colls]) (because an initial accumulator would be required in the variadic scenario).
So even if it was made variadic today, nothing about its current behavior would change.
It already is variadic (in the sense of supporting more than one arity):
=> (doc reduce)
-------------------------
clojure.core/reduce
([f coll] [f val coll])
f should be a function of 2 arguments. If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
applying f to that result and the 3rd item, etc. If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments. If coll has only 1 item, it
is returned and f is not called. If val is supplied, returns the
result of applying f to val and the first item in coll, then
applying f to that result and the 2nd item, etc. If coll contains no
items, returns val and f is not called.
If you made it variadic even further (e.g. allowing an arbitrary number of collections) then the 3 argument case would be ambiguous - did you mean to pass an initial value, or two different collections?
IMHO making functions variadic in more than one different aspect is something of an anti-pattern. Just because you can change the meaning of functions with arity overloads doesn't mean it is a good idea: you are often better off explicitly achieving the same effect by composing higher order functions.
OK, a fibonacci function in Clojure:
(defn give-fibs []
((fn fib-seq [a b]
(cons a (lazy-seq (fib-seq b (+ a b)))))
0 1))
Now, my question is, when I call it like so, I get an error :
(take 10 give-fibs)
edit, error is - java.lang.IllegalArgumentException: Don't know how to create ISeq from: four_cloj.core$give_fibs
However, it works when I call:
(take 10 (give-fibs))
When I check out what's going on, I can't really explain it:
(class (give-fibs)) ; clojure.lang.Cons
(class give-fibs) ; four_cloj.core$give_fibs
??
give-fibs is just that - the function itself. The concept of a function as a value that can be passed around (for example, as argument to take) takes some getting used to, but it's perfectly sensible and normal.
(give-fibs) is the result of calling give-fibs with no arguments, which is what you want in this context. The result is a list, and each element of a list is a Cons object, which is what class tells you.
In this expression you don't really call give-fibs:
(take 10 give-fibs)
you just pass the function itself to take. What you want is to actually call give-fibs in order to pass result of it to take:
(take 10 (give-fibs))
Remember that the first element in an s-expression is considered to be in function position, that is to say it will be executed. Therefore give-fibs and (give-fibs) are different in that the former is the actual function being passed to take and the latter is calling that function, and therefore returning the result to be passed to take.
Thats why (class give-fibs) is a function, and (class (give-fibs)) is a Cons cell as expected.
Just remember the first var after an opening bracket is in function position and will be executed, and its perfectly valid to pass an unexecuted function to another.
I was curious so I checked source code of swap! function on clojure repository, and it was like this:
(defn swap!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects. Returns
the value that was swapped in."
{:added "1.0"
:static true}
([^clojure.lang.Atom atom f] (.swap atom f))
([^clojure.lang.Atom atom f x] (.swap atom f x))
([^clojure.lang.Atom atom f x y] (.swap atom f x y))
([^clojure.lang.Atom atom f x y & args] (.swap atom f x y args)))
And I don't know what the ".swap" function is doing? I tried to search for it but it's not defined in the same file, so can't find definition of it. Or is this another special thing that is actually not a function at all?
A lot of things in Clojure are actually implemented in Java, like reference types (atom, ref, var, agent), data structures (map, vector, list, set), namespaces (the actual Namespace class) and other stuff. When reading the source code for Clojure functions in clojure.core it's not rare to find an interop call to a Java method.
It is my understanding that there's a long term objective to implement these things in Clojure itself (search here for Clojure-in-Clojure), but for now these things are implemented in Java, which is really not so bad since the whole language is open source and you can check any implementation detail online in the github repo (already linked in a comment): Clojure (note that there's a jvm and a clj folder indicating in what language the code inside is implemented).
In Clojure, an expression (.x object a b c) results in a method call, which in Java would be expressed as object.x(a, b, c). For more details see Java Interop.
In this particular case, the swap! function calls an appropriately overloaded swap method of the clojure.lang.Atom instance passed as the first argument. The swap method contains the actual logic performing the swap.
Simply put, an atom is sth. that is under control of transactional memory. (Read about STM, please.) It is inmutable for its observers (dereferencing an atom returns inmutable state as of the instant of deref), but can be mutated transactionally. This is exactly what swap! does. It transactionally swaps the old value for the new value. Please note that it is very different from assignment. Assignment is not safe for concurrency.
In other words, atom works like a cell in a database table. When you query it, you will have a value, not an exception, even if at the same instant another query is updating it.
Cheers -
In the accepted answer to another question, Setting Clojure "constants" at runtime the clojure function constantly is used.
The definition of constantly looks like so:
(defn constantly
"Returns a function that takes any number of arguments and returns x."
{:added "1.0"}
[x] (fn [& args] x))
The doc string says what it does but not why one would use it.
In the answer given in the previous question constantly is used as follows:
(declare version)
(defn -main
[& args]
(alter-var-root #'version (constantly (-> ...)))
(do-stuff))
So the function returned by constantly is directly evaluated for its result. I am confused as to how this is useful. I am probably not understanding how x would be evaluated with and without being wrapped in `constantly'.
When should I use constantly and why is it necessary?
The constantly function is useful when an API expects a function and you just want a constant. This is the case in the example provided in the question.
Most of the alter-* functions (including alter-var-root) take a function, to allow the caller to modify something based on its old value. Even if you just want the new value to be 7 (disregarding the old value), you still need to provide a function (providing just 7 will result in an attempt to evaluate it, which will fail). So you have to provide a function that just returns 7. (constantly 7) produces just this function, sparing the effort required to define it.
Edit: As to second part of the question, constantly is an ordinary function, so its argument is evaluated before the constant function is constructed. So (constantly #myref) always returns the value referenced by myref at the time constantly was called, even if it is changed later.