Combining 'let' and 'letfn' - clojure

I am trying to figure out the correct syntax for combing mutiple let and letfn statements inside a function body.
Local functions should be able to call each other recursively:
(defn func [input] (
let [local-var 3]
letfn [(do-local [a local]
(println a))]
(do-local input local-var)))
As it works fine for isolated local variable or function definition, combining let and letfn results in a compilation error:
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.core/letfn
Is there a way to get around that issue?
Edit:
Using (letfn [(local-var [_] (3))]) as factory function might still be another option, although it is just a workaround.
Edit 2:
I corrected the syntax error by placing letfn into a seperate inner scope, according to
(let ... (letfn ... ( execute functions)))
(defn func [input] (
let [local-var 3]
(letfn [(do-local [a local]
(println a))]
(do-local input local-var))))
Edit 3:
Switched to usage of let for local function definition, according to User xificurC's suggestion.
(defn func [input] (
let [local-var 3
do-local (fn [a local] (println a)) ]
(do-local input local-var)))

Related

Conditional "assignment" in functional programming

I am programming something that doesn't have side-effects, but my code is not very readable.
Consider the following piece of code:
(let [csv_data (if header_row (cons header_row data_rows) data_rows)]
)
I'm trying to use csv_data in a block of code. What is a clean way of conditioning on the presence of a header_row? I've looked at if-let, but couldn't see how that could help here.
I have run into similar situations with functional for-loops as well where I'm binding the result to a local variable, and the code looks like a pile of expressions.
Do I really have to create a separate helper function in so many cases?
What am I missing here?
Use the cond->> macro
(let [csv_data (cond->> data_rows
header_row (cons header-row)]
)
It works like the regular ->> macro, but before each threading form a test expression has to be placed that determines whether the threading form will be used.
There is also cond->. Read more about threading macros here: Official threading macros guide
First, don't use underscore, prefer dashes.
Second, there is nothing wrong with a little helper function; after all, this seems to be a requirement for handling your particular data format.
Third, if you can change your data so that you can skip those decisions and have a uniform representation for all corner cases, this is even better. A header row contains a different kind of data (column names?), so you might prefer to keep them separate:
(let [csv {:header header :rows rows}]
...)
Or maybe at some point you could have "headers" and "rows" be of the same type: sequences of rows. Then you can concat them directly.
The ensure-x idiom is a very common way to normalize your data:
(defn ensure-list [data]
(and data (list data)))
For example:
user=> (ensure-list "something")
("something")
user=> (ensure-list ())
(())
user=> (ensure-list nil)
nil
And thus:
(let [csv (concat (ensure-list header) rows)]
...)
i would propose an utility macro. Something like this:
(defmacro update-when [check val-to-update f & params]
`(if-let [x# ~check]
(~f x# ~val-to-update ~#params)
~val-to-update))
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> (:header :data1 :data2)
user> (let [header-row nil
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> [:data1 :data2]
it is quite universal, and lets you fulfill more complex tasks then just simple consing. Like for example you want to reverse some coll if check is trueish, and concat another list...
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows
(fn [h d & params] (apply concat (reverse d) params))
[1 2 3] ['a 'b 'c])]
csv-data))
;;=> (:data2 :data1 1 2 3 a b c)
update
as noticed by #amalloy , this macro should be a function:
(defn update-when [check val-to-update f & params]
(if check
(apply f check val-to-update params)
val-to-update))
After thinking about the "cost" of a one-line helper function in the namespace I've came up with a local function instead:
(let [merge_header_fn (fn [header_row data_rows]
(if header_row
(cons header_row data_rows)
data_rows))
csv_data (merge_header_fn header_row data_rows) ]
...
<use csv_data>
...
)
Unless someone can suggest a more elegant way of handling this, I will keep this as an answer.

clojure get args & exprs of received function

My question is: how can I get the args list and expressions of a received function ?
I'm trying to do something like this:
(defn first-fn [[args exprs]]
(println "Args:" args)
(println "Exprs:" exprs))
(first-fn (fn [a b c] (println "something")))
So, first-fn would print:
Args: [a b c]
Exprs: (println "something")
My goal is to create a macro that can use the args list of the received function.
Thank you.
Edit:
Use case:
I'm using compojure https://github.com/weavejester/compojure
You can define routes like this:
(GET "/:id" [id] (body_here id))
But I would like to change the syntax to be:
(defn handler-fn [id] (body_here id))
...
(GET "/:id" handler-fn)
So the handler (body) can be extracted from the routes, and might be reused as well.
I tried to reuse compile-route https://github.com/weavejester/compojure/blob/master/src/compojure/core.clj#L172
(defmacro MY_GET [path fn-src]
(let [fn-fn (second fn-src)
arg-vec (nth fn-src 2)
forms (drop 3 fn-src)]
(compojure.core/compile-route :get path arg-vec forms)))
But when I call:
(MY_GET "/:id" handler-fn)
It says: Don't know how to create ISeq from: clojure.lang.Symbol
You cannot do this with functions, you directly need a macro to do this and even then it is not straight-forward. First, let's explain the difference: macros are basically evaluated at compile-time and the result of this evaluation is then evaluated at run-time. The interesting part is that the evaluation at compile-time gets the literal, unevaluated arguments to the macro as data and not, like normal functions would, the evaluated arguments at run-time. So, your approach cannot work, because at the time first-fn receives it's arguments (at run-time), they are already evaluated -- in your example, first-fn receives nil as arguments. Cf. the documentation at clojure-doc for a much better explanation.
Now, solving your request with a macro requires the macro to parse the arguments (remember: at compile time, code is data) that it receives -- i.e. in your example, it needs to parse the sequence (fn [a b c] (println "something")) that builds up the function call you hand over to it. Probably you would want to cover other cases besides the fn one (e.g. the # short-hand), that's what it makes the problem not straight-forward in the general case.
This parsing could in the end be handled by a normal function parsing, e.g. a sequence. So, try solving a different puzzle first: build a function parse-code-sequence that takes a sequence (that looks like the functions you would hand over) and returns the args and expr -- note the quote (') in front of fn.
user> (parse-code-sequence '(fn [a b c] (println "something")))
{args: [a b c], expr: (println "something")}
Some hints to this: in the example here, which is showing the most used case, the sequence just consists of three elements and you don't need the first one. But the general case is a little bit more complex, cf. the official documentation on fn.
A final remark: when you implement the macro, you need to think about what it resolves to -- just adding the print-statements is easy, but do you also want to evaluate the arguments normally (so your macro becomes something like a debugging aid) or do you want to do something else?
Update to reflect your use-case
Your MY-GET macro is not doing what you think it's doing.
Take a look at the arguments that the macro gets: why do you think it can magically retrieve the function definition of handler-fn, when all that you give as argument to MY_GET is the symbol/var handler-fn? You would need to retrieve the source, but this usually will not be possible (cf. this SO question on retrieving the source of a function definition).
You are also missing a backquote before the call to compile-route: you want the call to compile-route to happen at run-time, not at compile time. Currently, the result of the macro evaluation is the result of the call to compile-route (at compile-time). Take a look at macroexpand which would show you the result of the macro-expansion. Basically, you want the macro to return the call to compile-route.
I don't see any easy way that you could accomplish what you look for. The argument vector of a route definition is defining what needs to be handed over. Even if you extract that to a function definition, compojure still needs to know what to hand over to that function.
Here is an example of what you could do.
(ns xyz
(:require
[tupelo.core :as t]
))
(t/refer-tupelo)
(spyx *clojure-version*)
(defmacro dissect [ fn-src ]
(let [fn-fn (first fn-src)
arg-vec (second fn-src)
forms (drop 2 fn-src) ]
(spyx fn-fn)
(spyx arg-vec)
(spyx forms)
; Here is the return value; ie the transformed code
`(defn my-fn
~arg-vec
(apply + ~arg-vec))))
; show the result
(newline)
(println
(macroexpand-1
'(dissect
(fn [a b c]
(println "the answer is")
42))))
; call it for real
(newline)
(dissect
(fn [a b c]
(println "the answer is")
42))
; use the generated function
(newline)
(spyx (my-fn 1 2 3))
with result:
*clojure-version* => {:major 1, :minor 8, :incremental 0, :qualifier nil}
fn-fn => fn
arg-vec => [a b c]
forms => ((println "the answer is") 42)
(clojure.core/defn tst.clj.core/my-fn [a b c] (clojure.core/apply clojure.core/+ [a b c]))
fn-fn => fn
arg-vec => [a b c]
forms => ((println "the answer is") 42)
(my-fn 1 2 3) => 6
Your project.clj needs the following to make spyx work:
:dependencies [
[tupelo "0.9.11"]

How can I get the var of a multimethod?

I'm trying to use dire to add hooks to multimethods. The author says it might not work.
Here is an example with a normal function:
(ns mydire.prehook
(:require [dire.core :refer [with-pre-hook!]]))
(defn times [a b]
(* a b))
(with-pre-hook! #'times
"An optional docstring."
(fn [a b] (println "Logging something interesting.")))
(times 21 2) ; => "Logging something interesting."
As you can see, with-pre-hook! is passed (var times) (which is the same as #'times).
The problem is that when calling var for a multimethod I'm getting an exception:
clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol
Is there a way to make this work?
Below is my code sample:
(defmulti get-url identity)
(defmethod get-url :stackoverflow
[site]
"http://stackoverflow.com")
(with-pre-hook! (var (get-method get-url :stackoverflow))
(fn [x] (println "getting url for stackoverflow.")))
var is a macro, it does not evaluate its argument. If you give it a list, it will not evaluate the list, it will reject it, because it's a list and not a symbol.
There is no var to attach to with a specific method, because defmethod does not create a var, it modifies the dispatch of the multimethod it is attached to. The value returned by get-method is a function, not a var.
Having looked at dire, it specifically needs a var to act on, and won't work on a specific method of a multimethod without some amount of redesign. So no, you can't use with-pre-hook on a specific method, though it might work on a multimethod itself (including all of its methods).

Trying to pass java member function as a var

If I run the following code in the REPL
(let [f '.startsWith] (f "abab" "a"))
it is evaluated to "a" instead of 'true'. Could someone please explain me this surprising result?
Actually, the real code, I want to make work is the following.
(defn set-up-bean! [bean functions-and-parameters]
(doseq [[f p] functions-and-parameters]
(f bean p))
(.init bean))
What I want to achieve is, to make the following two function calls do the same thing.
(set-up-bean! bean [['.setMember "a"]])
and
(do
(.setMember bean "a")
(.init bean))
One conventional approach is to use an anonymous function
(let [f (fn [a b] (.startsWith ^String a ^String b))] (f "abab" "a"))
...as this lets you type-hint parameters as-needed. You might also consider memfn:
(let [f (memfn startsWith String)] (f "abab" "a"))
In any event -- dot notation is syntactical sugar for interop, rather than providing real callable functions.

Why does dotrace throw a StackOverflowError here?

(use '[clojure.contrib.trace])
(dotrace [str] (reduce str [\a \b]))
In a nutshell:
That's because trace-fn-call, which is the thing dotrace uses to wrap the functions to be traced, uses str to produce the nice TRACE foo => val output.
Extended explanation:
The dotrace macro does its magic by installing a thread binding for each Var holding a function to be traced; in this case, there is one such Var, clojure.core/str. The replacement looks roughly like so:
(let [f ##'str]
(fn [& args]
(trace-fn-call 'str f args)))
The trace-fn-call, to quote its docstring, "Traces a single call to a function f with args.". In doing so, it calls the traced function, takes note of the return value, prints out a nice informative message of the form TRACE foo => val and returns the value obtained from the traced function so that regular execution may continue.
As mentioned above, this TRACE foo => val message is produced used str; however, in the case at hand, this is actually the function being traced, so a call to it leads to another call to trace-fn-call, which makes its own attempt to produce the tracing output string using str, which leads to another call to trace-fn-call... ultimately leading to the stack blowing up.
A workaround:
The following modified versions of dotrace and trace-fn-call should work fine even in the presence of weird bindings for core Vars (note that futures may not be scheduled promptly; if that's a problem, see below):
(defn my-trace-fn-call
"Traces a single call to a function f with args. 'name' is the
symbol name of the function."
[name f args]
(let [id (gensym "t")]
#(future (tracer id (str (trace-indent) (pr-str (cons name args)))))
(let [value (binding [*trace-depth* (inc *trace-depth*)]
(apply f args))]
#(future (tracer id (str (trace-indent) "=> " (pr-str value))))
value)))
(defmacro my-dotrace
"Given a sequence of function identifiers, evaluate the body
expressions in an environment in which the identifiers are bound to
the traced functions. Does not work on inlined functions,
such as clojure.core/+"
[fnames & exprs]
`(binding [~#(interleave fnames
(for [fname fnames]
`(let [f# #(var ~fname)]
(fn [& args#]
(my-trace-fn-call '~fname f# args#)))))]
~#exprs))
(Rebinding trace-fn-call around a regular dotrace apparently doesn't work; my guess is that's because of clojure.* Var calls still being hard-wired by the compiler, but that's a separate matter. The above will work, anyway.)
An alternative would be to use the above my-dotrace macro together with a my-trace-fn-call function not using futures, but modified to call custom replacements for the clojure.contrib.trace functions using the following in place of str:
(defn my-str [& args] (apply (.getRoot #'clojure.core/str) args))
The replacements are straightforward and tedious and I omit them from the answer.