I seem to be coming up with too many multi-dispatch functions and would like to reduced the number. The approach I am currently using is to have a multi-function call another multifunction but that seems wrong. Here is an example of what I would like:
(defmulti foo
(fn [bar baz] [(:type bar) (:x baz) (:y bar)]))
(defmethod foo [:z :y _] [bar baz] (<I will take anything for the 3rd element>))
(defmethod foo [:z _ :x] [bar baz] (<I will take anything for the 2nd element>))
(defmethod foo [:z :y :x] [bar baz] (<I must have this dispatch value>))
...
The basic idea being that in the first case the method does not care what the third element of the dispatch value is, it will take them anything, but the second element must be :y.
Is something like this possible in clojure?
I may be asking for predicate dispatch, which, I guess, is still a work in progress.
Seems you need proper pattern matching, in which case core.match is what you want.
Using core.match, you can write something like:
(match [(:type bar) (:x baz) (:y bar)]
[:z :y _] ;; accepts anything as the 3rd element
[:z _ :x] ;; accepts anything as the 2nd element
[:z :y :x] ;; accepts precisely these values
)
Related
To build up a data structure I find myself doing a lot of things like:
(let [foo (atom [])]
(do
(swap! foo conj {:foo "bar"})
(swap! foo conj {:foo "baz"}))
#foo)
=> [{:foo "bar"} {:foo "baz"}]
Is this an anti-pattern? I'm using a lot of atoms.
No need for an atom here. You can use immutable data structures:
(-> []
(conj {:foo "bar"})
(conj {:foo "baz"}))
;;=> [{:foo "bar"} {:foo "baz"}]
For folks coming from OOP or imperative languages, this is probably the hardest shift: avoiding mutability.
First off, you don't need the do since it is implied inside let. Then, for this example, plain old -> works great (using my favorite template project):
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn stuff
[]
(-> []
(conj {:foo "bar"})
(conj {:foo "baz"})))
(dotest
(is= (stuff)
[{:foo "bar"}
{:foo "baz"}])
Another option is to user reduce:
(defn save-odds
[accum arg]
(if (odd? arg)
(conj accum arg)
accum))
<snip>
(is= (reduce save-odds
[]
(range 6))
[1 3 5]))
Having said that, there is nothing wrong IMHO with using an atom as an accumulator. It is simple & straightforward. And, if the "nasty" mutation of the atom never leaks outside of your function, it cannot cause any complexity in the rest of the program.
"If mutation occurs and no outside function is affected, does
it really matter?"
After all, reduce and friends also use mutation internally, and they are excellent examples of "functional" programming. That is, they are pure functions (have referential transparency), and cause no side effects.
The more I write in Clojure, the more I come across the following sort of pattern:
(defn mapkeys [foo bar baz]
{:foo foo, :bar bar, :baz baz})
In a certain sense, this looks like the inverse process that a destructuring like
(let [{:keys [foo bar baz]}] ... )
would achieve.
Is there a "built-in" way in Clojure to achieve something similar to the above mapkeys (mapping name to keyword=>value) - perhaps for an arbitrary length list of names?
No such thing is built in, because it doesn't need to be. Unlike destructuring, which is fairly involved, constructing maps is very simple in Clojure, and so fancy ways of doing it are left for ordinary libraries. For example, I long ago wrote flatland.useful.map/keyed, which mirrors the three modes of map destructuring:
(let [transforms {:keys keyword
:strs str
:syms identity}]
(defmacro keyed
"Create a map in which, for each symbol S in vars, (keyword S) is a
key mapping to the value of S in the current scope. If passed an optional
:strs or :syms first argument, use strings or symbols as the keys instead."
([vars] `(keyed :keys ~vars))
([key-type vars]
(let [transform (comp (partial list `quote)
(transforms key-type))]
(into {} (map (juxt transform identity) vars))))))
But if you only care about keywords, and don't demand a docstring, it could be much shorter:
(defmacro keyed [names]
(into {}
(for [n names]
[(keyword n) n])))
I find that I quite frequently want to either construct a map from individual values or destructure a map to retrieve individual values. In the Tupelo Library I have a handy pair of functions for this purpose that I use all the time:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(let [m {:a 1 :b 2 :c 3}]
(with-map-vals m [a b c]
(spyx a)
(spyx b)
(spyx c)
(spyx (vals->map a b c)))))
with result
; destructure a map into values
a => 1
b => 2
c => 3
; construct a map
(vals->map a b c) => {:a 1, :b 2, :c 3}
P.S. Of course I know you can destructure with the :keys syntax, but it always seemed a bit non-intuitive to me.
I'm trying to remove an element from a list or set, like this:
(remove :Veronica (list :Veronica :Hailey))
It does not work, so, I went to remove documentation, that says I need to pass a predicate to the remove function. But the following code works:
(remove #{:foo} [:foo :bar])
(remove #{:foo} (list :foo :bar))
#{:foo} is a set. Why does it works?
Is a set a function?
Thanks
Why does the following
(remove :Veronica (list :Veronica :Hailey))
;(:Veronica :Hailey)
pass muster?
have no apparent effect?
A keyword such as :Veronica is a function accepting one argument, hence can be used as a predicate.
But ...
It expects a map or set argument.
It then looks itself up in the argument.
For example,
(:Veronica #{:Veronica})
;:Veronica
(:Veronica #{1 2 "Buckle my shoe"})
;nil
It also forgives useless arguments:
(:Veronica 4) ; 4 is not a map or set.
;nil
So (:Veronica :Veronica) and (:Veronica :Hailey) are both nil, so the remove in
(remove :Veronica (list :Veronica :Hailey))
... accomplishes nothing, since the predicate always evaluates false(ish).
The other solutions explain why
(remove #{:foo} (list :foo :bar))
... has the effect you are looking for.
Sets are functions of their members. If the argument is a member of the set, the member is returned. If the argument is not a member of the set, nil is returned. This is documented towards the end of the Data Structures page of the Clojure documentation. To quote that page:
(def s #{:a :b :c :d})
(s :b)
-> :b
(s :k)
-> nil
Clojure set and map can be work as a function.
user=> (#{:foo} :foo)
:foo
user=> (#{:foo} :bar)
nil
So, in your code, #{:foo} work as a function, and basically the same as
(remove (fn [x] (#{:foo} x)) [:foo :bar])
For more detail, you can read this: Clojure: Using Sets and Maps as Functions.
Is there any way to reuse a destructuring between multiple methods in a multimethod?
(defmulti foo (fn [x] (:a x)))
(defmethod foo :1 [{:keys [a b c d e]}] (str a b c d e))
(defmethod foo :2 [a] "")
(defmethod foo :3 [a] "")
Now this is a trivial example, but imagine we have a much more complicated destructuring with nested maps and I want to use it on all my defmethods for foo. How would I do that?
A practical solution would be to only use the keys that you need for each individual method. An important thing to note about destructuring is that you don't have to bind every value in the collection you're destructuring. Let's say every map passed to this multimethod contains the keys :a through :e, but you only need a couple of those keys per method. You could do something like this:
; note: a keyword can act as a function; :a here is equivalent to (fn [x] (:a x))
(defmulti foo :a)
(defmethod foo :1 [{:keys [a b c d e]}] (str a b c d e))
(defmethod foo :2 [{:keys [b d]}] (str b d))
(defmethod foo :3 [{:keys [c e a]}] (str a c e))
If you have a complicated nested structure and you want to grab specific values, you can just leave out the keys you don't need, or alternatively, depending on your use case, a let binding within the function definition might end up being easier to read. Steve Losh's Caves of Clojure comes to mind -- in writing a roguelike text adventure game from scratch in Clojure, he used nested maps to represent the state of a game. Initially he wrote some of the functions using destructuring to access the inner bits of the "game state" map, e.g.:
(defmethod draw-ui :play [ui {{:keys [tiles]} :world :as game} screen]
...
But then later, he decided to make this code more readable by pulling the destructuring out into a let binding:
(defmethod draw-ui :play [ui game screen]
(let [world (:world game)
tiles (:tiles world)
...
The point is, if you're working with a deeply nested structure and you want to keep your code simple (especially if you're writing a multimethod with several methods taking that same structure as an argument), you may want to avoid using destructuring and just use let bindings to grab the pieces you want. get-in is a good tool for concisely getting values from nested collections. Going back to the Caves of Clojure example, if Steve just needed the tiles, he could have done something like this:
(defmethod draw-ui :play [ui game screen]
(let [tiles (get-in game [:world :tiles])
...
Personally, I find that much easier to read than mucking up the function arguments with {{:keys [tiles]} :world :as game}.
EDIT:
If you really want to avoid having to repeat the destructuring for each multimethod, and you want each method to have the same bindings available, you could write a macro:
(defmulti foo :a)
(defmacro deffoomethod [dispatch-val & body]
`(defmethod foo ~dispatch-val [{:keys [~'a ~'b ~'c ~'d ~'e]}]
~#body))
(deffoomethod 1 (str a b c d e))
(deffoomethod 2 (str b d))
(deffoomethod 3 (str a c e))
(foo {:a 1 :b 2 :c 3 :d 4 :e 5})
;=> "12345"
(foo {:a 2 :b \h :d \i})
;=> "hi"
(foo {:a 3 :b \x :c 0 :d \x :e 0})
;=> "300"
I wouldn't recommend this approach, though, as it breaks macro hygiene. Anyone using this macro has to remember that it binds the symbols a through e to the corresponding keys in the argument, and that could be problematic.
So I thought it would be a nice idea to name a function that calculates the exponential ^, but it seems like the caret actually does something special, as the Clojure REPL generates an error when evaluating '^. Googling mostly gave me this, so I was wondering what the actualy use for the caret in Clojure is.
(Also, would it be possible after all to name a function ^?)
^ is "the meta character" it tells the reader to add the symbol starting with ^ as metadata to the next symbol (provided it is something that implements IMetas)
user=> (def x ^:IamMeta [1 2 3])
#'user/x
user=> x
[1 2 3]
user=> (meta x)
{:tag :IamMeta}
user=>
You can learn a lot about how clojure works under the hood by looking at the meta of things, for instance functions:
user=> (meta foo)
{:ns #<Namespace user>,
:name foo, :file "NO_SOURCE_PATH",
:line 5, :arglists ([s])}
this is very often used for type hints
(defn foo [^String s] (.charAt s 1))
it is generally a good idea to turn on reflection warnings (set! *warn-on-reflection* true) and then add type hints until the warnings go away. without these Clojure will look up the type of the function operands at run-time, which saves you the trouble of fussing with types though at a slight cost.
PS: My next favorite reader character is the "dispatch" character #, it is well worth learning about it next :)
PPS: this is different in clojure 1.2.x vs clojure 1.3.x
in Clojure 1.2.1 metadata does not compose when you use the meta-character:
user=> (def foo ^:foo ^:bar [1 2 3])
#'user/foo
user=> (meta foo)
{:tag :foo}
and in 1.3 it "does the right thing" and also keywords are options instead of "tags":
user=> (def foo ^:foo ^:bar [1 2 3])
#'user/foo
user=> (meta foo)
{:foo true, :bar true}
It seems to me that the answer to your question is, unfortunately, no. In Clojure, you cannot name a function ^.
I tried the following in the REPL:
user=> (println \^)
^
nil
This seems to imply that you can escape the carat (^) with a backslash. However, if I try to declare a function using \^ as a name then I get an error message:
user=> (defn \^ [n e] (cond (= e 0) 1 :else (* n (\^ n (- e 1)))))
IllegalArgumentException First argument to defn must be a symbol
clojure.core/defn (core.clj:277)
The same code works with a regular text name:
user=> (defn exp [n e] (cond (= e 0) 1 :else (* n (exp n (- e 1)))))
#'user/exp
user=> (exp 3 3)
27
I would be delighted if someone with better Clojure-fu than mine could prove me wrong! :)