I have the following code for a generic conversion library:
(defn using-format [format] {:format format})
(defn- parse-date [str format]
(.parse (java.text.SimpleDateFormat. format) str))
(defn string-to-date
([str]
(string-to-date str (using-format "yyyy-MM-dd")))
([str conversion-params]
(parse-date str (:format (merge (using-format "yyyy-MM-dd") conversion-params)))))
I need to be able to call it like this:
(string-to-date "2011-02-17")
(string-to-date "2/17/2011" (using-format "M/d/yyyy"))
(string-to-date "2/17/2011" {})
The third case is somewhat problematic: the map does not necessarily contain the key :format which is critical for the function. That's why the merge with default value.
I need to have a dozen of similar functions for conversions between all other types. Is there a more elegant way that would not require me to copy-paste, use merge etc. in every single function?
Ideally, looking for something like this (macro?):
(defn string-to-date
(wrap
(fn [str conversion-params]
(parse-date str (:format conversion-params))) ; implementation
{:format "yyyy-MM-dd"})) ; default conversion-params
... that would produce an overloaded function (unary and binary), with binary having a merge like in the first example.
So to define this a little more strictly, you want to create a macro that creates converter functions. A converter function is a function with two arities, one argument and two arguments. The first argument to a converter function is the object to be converted. The second argument is a map of options, that will somehow affect the conversion (like a format string in your example.)
A default parameter map can be specified. When called with one argument, a converter function will use the default parameter map. When called with two arguments, a converter function will merge the default parameter map with the passed in parameter map, such that the passed in parameters override the defaults if they exist.
Let's call this macro def-converter. Def converter will take three arguments, the first is the name of the function to be created. The second is an anonymous function of two arguments that implements the two-arity converter, without the default parm merging. The third argument is the default parm map.
Something like this will work:
(defmacro def-converter [converter-name converter-fn default-params]
(defn ~converter-name
([to-convert#]
(let [default-params# ~(eval default-params)]
(~converter-fn to-convert# default-params#)))
([to-convert# params#]
(let [default-params# ~(eval default-params)]
(~converter-fn to-convert# (merge default-params# params#))))))
Then you can use it like:
(def-converter
string-to-date
(fn [to-convert conversion-params]
(parse-date to-convert conversion-params))
(using-format "yyyy-MM-dd"))
But you have to make a change to one of your helper functions:
(defn- parse-date [str params]
(.parse (java.text.SimpleDateFormat. (:format params)) str))
This is because the macro needs to be general enough to handle arbitrary maps of parameters, so we can't count on. There are probably ways around it, but I can't think of one offhand that's not messier than just pushing that off onto a helper function (or the anonymous function that needs to be passed into def-converter).
clojure.contrib.def/defnk is handy if you need functions with default keyword arguments:
(use 'clojure.contrib.def)
...
(defnk string-to-date [str :format "yyyy-MM-dd"]
(parse-date str format))
(string-to-date "2011-02-17")
(string-to-date "2/17/2011" :format "M/d/yyyy")
For the record, here's what I figured out later at night:
(defmacro defconvert [name f default]
`(defn ~name
([v#] (~name v# ~default))
([v# conversion-params#] (~f v# (merge ~default conversion-params#)))))
It seems to work and generate exactly the definition I had up there. I it possible with defnk or some other built-in mechanism, having a map of default values and accepting override of some but not necessarily all?
Related
If I have an hash-map and I want to assoc a value to it, and I get the key as an argument, what should i do?
(defn define [name type kind] "define new var in one of the tables"
(if (or (= type "static") (= type "field"))
(def classScope (assoc classScope name (list type kind (addCount kind))))
(def methodScope (assoc methodScope name (list type kind (addCount kind))))
)
)
My problem is that i can't use :name, and not 'name.
Thanks!!
Update: If you want your keys to be in keyword form, just call keyword on them....
(defn my-map-fn [name type kind]
(assoc some-map (keyword name) (some-fn type kind)))
e.g.
(my-map-fn "some-name" "some-type" "some-kind") => {:some-name some-val}
Note that you shouldn't use def inside of defn. It looks like you want to keep a map of data and as you call define you want to store some more data in that map. A way that I go about this is to use atoms (there are other ways too).
(defonce classScope (atom {})
(defonce methodScope (atom {}))
(defn define
"if you want a doc string it goes here"
[name type kind]
(swap! (if (#{"static" "field"} type) classScope methodScope)
#(assoc % name (list type kind (addCount kind)))))
the benefit here is you get atomic updates and calls to define that may happen really close together won't butt heads.
Let's start with your explanatory comment:
I'm trying to create an hash-map that's like a symbol table: I will
identify each variable by its name, and it will have a list with its
type, kind and index in the code.
A few thoughts:
Don't use a list for the variable's characteristics; use a map.
You can think of the name of a variable as
a plain old string
a symbol
a keyword
Any of these works as the key of a map entry. Keep it simple. Use a string.
You're going to need such a table for every scope. And a scope should know its enclosing scope.
The descriptors static and field are not types; nor are they
alternatives in - say - Java.
I suggest you look at clojure.spec and typed Clojure to see how similar problems are handled within Clojure.
So I'm trying to make a Clojure macro that makes it easy to interop with Java classes utilizing the Builder pattern.
Here's what I've tried so far.
(defmacro test-macro
[]
(list
(symbol ".queryParam")
(-> (ClientBuilder/newClient)
(.target "https://www.test.com"))
"key1"
(object-array ["val1"])))
Which expands to the below
(.
#object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "org.glassfish.jersey.client.JerseyWebTarget#107a5073"]
queryParam
"key1"
#object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;#16751ba2"])
The desired result is:
(.queryParam
#object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "org.glassfish.jersey.client.JerseyWebTarget#107a5073"]
"key1"
#object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;#16751ba2"])
I guess the . is causing something to get evaluated and moved around? In which case the solution would to be to quote it. But how can I quote the results of an evaluated expression?
My goal is to convert maps into code that build the object by have the maps keys be the functions to be called and the values be the arguments passed into the Java functions.
I understand how to use the threading and do-to macros but am trying to make request building function data driven. I want to be able take in a map with the key as "queryParam" and the values as the arguments. By having this I can leverage the entirety on the java classes functions only having to write one function myself and there is enough of a 1 to 1 mapping I don't believe others will find it magical.
(def test-map {"target" ["https://www.test.com"]
"path" ["qa" "rest/service"]
"queryParam" [["key1" (object-array ["val1"])]
["key2" (object-array ["val21" "val22" "val23"])]] })
(-> (ClientBuilder/newClient)
(.target "https://www.test.com")
(.path "qa")
(.path "rest/service")
(.queryParam "key1" (object-array ["val1"]))
(.queryParam "key2" (object-array ["val21" "val22" "val23"])))
From your question it's not clear if you have to use map as your builder data structure. I would recommend using the threading macro for working directly with Java classes implementing the builder pattern:
(-> (ClientBuilder.)
(.forEndpoint "http://example.com")
(.withQueryParam "key1" "value1")
(.build))
For classes that don't implement builder pattern and their methods return void (e.g. setter methods) you can use doto macro:
(doto (Client.)
(.setEndpoint "http://example.com")
(.setQueryParam "key1" "value1"))
Implementing a macro using a map for encoding Java method calls is possible but awkward. You would have to keep each method arguments inside a sequence (in map values) to be a able to call methods with multiple parameters or have some convention for storing arguments for single parameter methods, handling varargs, using map to specify method calls doesn't guarantee the order they will be invoked etc. It will add much complexity and magic to your code.
This is how you could implement it:
(defmacro builder [b m]
(let [method-calls
(map (fn [[k v]] `(. (~(symbol k) ~#v))) m)]
`(-> ~b
~#method-calls)))
(macroexpand-1
'(builder (StringBuilder.) {"append" ["a"]}))
;; => (clojure.core/-> (StringBuilder.) (. (append "a")))
(str
(builder (StringBuilder.) {"append" ["a"] }))
;; => "a"
My question is about structuring lisp code with side effects. The particular example I have in mind comes from Clojure, but I think it can apply to any lisp.
In this case, I am interacting with an existing library that requires some functions to be called in a particular order. The final function call creates the value I need for the rest of the procedure.
The code looks like this:
(defn foo []
(let [_ procedure-with-side-effect
__ another-procedure-with-side-effect
value procedure-that-creates-the-value]
(do-something value)))
This works and everything is great, except I think the let block looks hideous. Is there a better way to do this?
If you don't need the intermediate values of the function calls, you can just put a bunch of function calls in the body of the defn:
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(do-something (procedure-that-creates-the-value)))
While this is the best for this code, there are other options. You can also put any number of function calls in the body of a let:
(let [val 3]
(fun-call-1)
(fun-call-2)
(fun-call-3 val))
And if you don't want to bind any values, you can use do:
(do (fun-call-1)
(fun-call-2)
(fun-call-3))
In Lisp every function body is a ordered set of forms. The value(s) of the last form will be returned. If the procedures don't use intermediate result values as arguments, a LET is not necessary. If the procedure-that-creates-the-value does not need to be documented by naming a variable, the LET binding for its value is also not necessary.
So in Lisp the code is just this:
(defun foo ()
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(do-something (procedure-that-creates-the-value)))
I'm not super experienced, but I'd do it this way:
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(let [value (procedure-that-creates-the-value)]
(do-something value)))
or
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(-> (procedure-that-creates-the-value)
do-something))
or
(defn foo []
(procedure-with-side-effect)
(another-procedure-with-side-effect)
(do-something (procedure-that-creates-the-value)))
Edit: defn expressions are wrapped with an implicit do.
I need a function that thinly wraps amazonica's sqs/receive-message in order to add a default wait time. The function requires a queue URL, and then accepts any number of optional named parameters, which should be passed along to sqs/receive-message untouched. I would like to call it like this:
(my-receive-message "https://sqs.us-east-1.amazonaws.com/123/test-q"
:max-number-of-messages 10
:delete true)
This should result in a call to sqs/receive-message like this:
(sqs/receive-message :queue-url "https://sqs.us-east-1.amazonaws.com/123/test-q"
:wait-time-seconds 20
:max-number-of-messages 10
:delete true)
This is something I find myself wanting to do fairly often, but I haven't found a nice way yet. Is there an idiomatic way to do this?
Use apply over the merged parameters.
(defn my-receive-message
[url & {:as args}]
(apply sqs/receive-message (-> {:queue-url url
:wait-time-seconds 20}
(merge args)
seq
flatten)))
You could always write a macro:
(defmacro my-receive-message [url & opts]
`(sqs/receive-message
~#(flatten (seq (merge {:queue-url url :wait-time-seconds 20}
(apply hash-map opts))))))
(Note that this does pretty much exactly the same thing as Guillermo's function. The main difference is that you don't have to apply sqs/receive-message -- the unquote-splicing (~#) takes care of the apply part implicitly.)
I just started to learn clojure and I don't have much functional programming experience. Let's say I have a function :
(defn process-seq
[process]
...doing something...)
that takes another function as an argument. This argument should be a function that takes single argument - a sequence. For example :
(defn filter-odd
[sequence]
(filter odd? sequence))
So I can now write :
(process-seq filter-odd)
What I don't like about it is that I had to define filter-odd function. I would like to achieve it without defining it. All I want is to pass filter function with constant predicate : odd?.Something like (just a pseudo code that I made up) :
(process-seq filter(odd?))
Is something like that possible?
You can pass an anonymous function as parameter:
(process-seq (fn [sequence] (filter odd? sequence)))
Or even shorter:
(process-seq #(filter odd? %))
Or as mentioned by A.Webb in the comments, we could use partial:
(process-seq (partial filter odd?))