I am using a map to store function calls (in ClojureScript, but it should be the same in Clojure) :
(def parse-fn {:json js/JSON.parse
:edn read-string
:transit t->clj})
I then have a transform function like the following :
(defn transform [format data]
((get parse-fn format) data))
Calling transform like that : (transform :transit data) works nicely. I am only worried about the readability of the transform function. It is not immediately obvious that the first thing is a function.
Edit : I cannot just call (:transit parse-fn) since the format comes from another function, as in : (another-fn [... format ...] ... (transform format data))
Is there an explicit call function, or is the structure of this code not idiomatic ?
Your initial design is completely readable. Passing functions to other functions that do the actual work is a fairly normal way of doing things. I don't think you need to change anything to "make it more readable". If i was going to change anything to make i more clrearly spelled out would be to change the name of transform to transform-lookup or get-transform
(another-fn [... format ...] ... (transform-lookup format data))
or
(another-fn [... format ...] ... (get-transform format data))
Though this is a very minor distinction and it's fine the way it is.
Maybe consider treating the keyword as the function.
((:transit parse-fn) data)
To me the more terse approach helps me recognise the first element as distinct.
Alternatively, you can treat the map as the function instead.
((parse-fn :transit) data)
As I understand, the first approach is generally considered more idiomatic and it can also be optimized more intelligently.
Related
say there's a library l, which has two functions (a and b).
Calling both functions and merging the results into a vector could be done like this:
(concat (l/a) (l/b))
Is there a way to make this more generic? I tried something like this, but it threw an exception:
(apply concat (map #(l/%) ['a 'b]))
of course, this would work:
(apply concat [l/a l/b])
Calling both functions and merging the results into a vector could be done like this:
(concat (l/a) (l/b))
No, you will not get a vector. And you will only get a sequence if those functions return sequences. Otherwise, definitely not, you will get a runtime exception with this code and your assumption.
It sounds like you have a bunch of functions and you want to concatenate the results of them all together? There is no need to quote them, just make a sequence of the functions:
[l/a l/b l/c ...]
And use apply with concat as you already are, or use reduce to accumulate values.
Call vec on the result if you need it to be a vector rather than a sequence.
Your other solutions are definitely making your code much much more complex, unnecessary, and difficult to read. (also, you almost never need to quote vars as you are doing)
It looks like you want a general way of invoking a function inside a namespace. You can construct a symbol and dereference it to find the functions, then combine the results using mapcat e.g.
(mapcat #((find-var (symbol "l" %))) ["a" "b"])
alternatively you could first find the namespace and use ns-resolve to find the vars e.g.
(let [ns (find-ns 'l)]
(mapcat #((ns-resolve ns %)) ['a 'b]))
I have a large lazy seq of lines that I want to write to a file. In C#, I would use System.IO.File/WriteAllLines which has an overload where the lines are either string[] or IEnumerable<string>.
I want to do this without using reflection at runtime.
(set! *warn-on-reflection* true)
(defn spit-lines [^String filename seq]
(System.IO.File/WriteAllLines filename seq))
But, I get this reflection warning.
Reflection warning, ... - call to WriteAllLines can't be resolved.
In general I need to know when reflection is necessary for performance reasons, but I don't care about this particular method call. I'm willing to write a bit more code to make the warning go away, but not willing to force all the data into memory as an array. Any suggestions?
Here are two options to consider, depending on whether you are using Clojure's core data structures.
Convert from a seq to an IEnumerable<string> with Enumerable.Cast from LINQ
This option will work for any IEnumerable that contains only strings.
(defn spit-lines [^String filename a-seq]
(->> a-seq
(System.Linq.Enumerable/Cast (type-args System.String))
(System.IO.File/WriteAllLines filename)))
Type Hint to force the caller to supply IEnumerable<string>
If you want to use a type hint, do this. But watch out, the clojure data structures do not implement IEnumerable<String>, so this could lead to a runtime exception.
^|System.Collections.Generic.IEnumerable`1[System.String]|
Wrapping the full CLR name of the type in vertical pipes (|) lets you specify characters that are otherwise illegal in Clojure syntax.
(defn spit-lines [^String filename ^|System.Collections.Generic.IEnumerable`1[System.String]| enumerable-of-string]
(System.IO.File/WriteAllLines filename enumerable-of-string))
Here's the exception from (spit-lines "filename.txt" #{}) when passing a set to the type-hinted version:
System.InvalidCastException: Unable to cast object of type 'clojure.lang.PersistentTreeSet' to type 'System.Collections.Generic.IEnumerable`1[System.String]'.
More information about specifying types.
Problem:
Suppose I have a set of functions f_1 ... f_n that I want to compose at runtime, such that I get for example:
(f_a (f_b (f_c) (f_d)) (f_e))
Therefore I need the types of the parameters and the return value of each function in order to know, which functions I can plug into each other.
First Attempt: Annotate each function
(defn foo [n f]
^{:params [Number clojure.lang.Fn]
:return String}
(do stuff with f and n, return a string))
I don't like this approach because of obvious reasons, such as if I wanted to use clojure.core as the set of functions I would have to annotate every function, which wouldn't be very desirable.
Questions
How would you attempt to solve this problem?
Could core.typed help me with that?
I do similar things when composing the search query to pass to solr and use a map for the arguments to all the functions and have them all return a map with whatever changes the function decided to make contained in the map, and everything else returned unchanged. So in short i would:
use a map for the input and output of every function
core.typed is not helpful because everything is a map
prismatic.schema is very helpful because you can use it to know what keys are required and to write tests that validate the structure of these maps.
Projects/people with more of an inclination to statically typed functional languages will likely turn to monads in cases like this because they are a powerful tool for describing composition in a type safe way.
I have a defrecord called a bag. It behaves like a list of item to count. This is sometimes called a frequency or a census. I want to be able to do the following
(def b (bag/create [:k 1 :k2 3])
(keys bag)
=> (:k :k1)
I tried the following:
(defrecord MapBag [state]
Bag
(put-n [self item n]
(let [new-n (+ n (count self item))]
(MapBag. (assoc state item new-n))))
;... some stuff
java.util.Map
(getKeys [self] (keys state)) ;TODO TEST
Object
(toString [self]
(str ("Bag: " (:state self)))))
When I try to require it in a repl I get:
java.lang.ClassFormatError: Duplicate interface name in class file compile__stub/techne/bag/MapBag (bag.clj:12)
What is going on? How do I get a keys function on my bag? Also am I going about this the correct way by assuming clojure's keys function eventually calls getKeys on the map that is its argument?
Defrecord automatically makes sure that any record it defines participates in the ipersistentmap interface. So you can call keys on it without doing anything.
So you can define a record, and instantiate and call keys like this:
user> (defrecord rec [k1 k2])
user.rec
user> (def a-rec (rec. 1 2))
#'user/a-rec
user> (keys a-rec)
(:k1 :k2)
Your error message indicates that one of your declarations is duplicating an interface that defrecord gives you for free. I think it might actually be both.
Is there some reason why you cant just use a plain vanilla map for your purposes? With clojure, you often want to use plain vanilla data structures when you can.
Edit: if for whatever reason you don't want the ipersistentmap included, look into deftype.
Rob's answer is of course correct; I'm posting this one in response to the OP's comment on it -- perhaps it might be helpful in implementing the required functionality with deftype.
I have once written an implementation of a "default map" for Clojure, which acts just like a regular map except it returns a fixed default value when asked about a key not present inside it. The code is in this Gist.
I'm not sure if it will suit your use case directly, although you can use it to do things like
user> (:earth (assoc (DefaultMap. 0 {}) :earth 8000000000))
8000000000
user> (:mars (assoc (DefaultMap. 0 {}) :earth 8000000000))
0
More importantly, it should give you an idea of what's involved in writing this sort of thing with deftype.
Then again, it's based on clojure.core/emit-defrecord, so you might look at that part of Clojure's sources instead... It's doing a lot of things which you won't have to (because it's a function for preparing macro expansions -- there's lots of syntax-quoting and the like inside it which you have to strip away from it to use the code directly), but it is certainly the highest quality source of information possible. Here's a direct link to that point in the source for the 1.2.0 release of Clojure.
Update:
One more thing I realised might be important. If you rely on a special map-like type for implementing this sort of thing, the client might merge it into a regular map and lose the "defaulting" functionality (and indeed any other special functionality) in the process. As long as the "map-likeness" illusion maintained by your type is complete enough for it to be used as a regular map, passed to Clojure's standard function etc., I think there might not be a way around that.
So, at some level the client will probably have to know that there's some "magic" involved; if they get correct answers to queries like (:mars {...}) (with no :mars in the {...}), they'll have to remember not to merge this into a regular map (merge-ing the other way around would work fine).
I cannot use logical functions on a range of booleans in Clojure (1.2). Neither of the following works due to logical functions being macros:
(reduce and [... sequence of bools ...])
(apply or [... sequence of bools ...])
The error message says that I "can't take value of a macro: #'clojure.core/and". How to apply these logical functions (macros) without writing boilerplate code?
Don't -- use every? and some instead.
Michal's answer is already spot on, but the following alternative approach can be useful in similar situations whenever you want to use a macro as a function:
(reduce #(and %1 %2) [... sequence of bools ...])
Basically you just wrap the macro in an anonymous function.
There are a couple of good reasons to consider this approach:
There are situations where a handy function like some or every? does not exist
You may get better performance (reduce is likely to benefit from some very good optimisations in the future, for example applying the function directly to a vector rather than converting the vector into a sequence)