Reputation: 1028
The more I think about this problem, the more wrong it seems...
I have defined in my program something like a 'map constructor'. The idea behind this is that I have a generic map structure to handle some 'items' but I want to enforce some defaults for specific kind of items.
The problem that I have is that this 'map constructor' has a k-v pair, and that pair's value should be determined by the function that consumes this map (it might get clearer in the following example).
My first idea was to quote an expression in the value and then do an eval
on it in the said function. The second idea was to replace the value with a fn
, but this seems to return something similar to the quoted expression.
Let me try to depict the problem:
The constructor is something like
(defn cons-field [b]
{:a (fn [name] (str name "!"))
:b b
:c "default"})
The item is created (def a-field (cons-field 5))
The calling function that consumes the map is something like
(defn the-function [name field]
(str (get-in field [:a])))
Now what I need is this :a's value to be a function of the parameter name in 'the-function'. Of course the last function is not working and I'm not sure if it's the correct approach anyway. The ':a' key is not always a fn
; sometimes it's just a string literal. Any ideas?
Cheers!
Upvotes: 2
Views: 187
Reputation: 9266
It is not really possible to understand your problem based on what you have posted. All I can do for you is tell you what your provided code does and guess what you would want it to do.
(def r (cons-field 5))
creates a hash-map r
with (r :b)
= 5, (r :c)
= "default" and (r :a)
= (fn [name] (str name "!"))
. As you can see, neither the result of (r :a)
nor the result of (r :c)
is related to r or 5.
If the results described above is what you want, it would be only logical to do some refactoring:
(def default-field {:a (fn [name] (str name "!"))
:c "default"})
(def cons-field (partial assoc default-field :b))
Regarding the-function
: A call to (get-in field [:a])
is the same as (field :a)
and will return the function (fn [name] ...)
. the-function
will stringify this fn using str and most likely return something like "user.fn$23092...."
If you want the-function
to return the result of calling (fn [name] ...)
with name as passed to the function, you need to change your the-function
as follows:
(defn the-function
[name field]
(str ((field :a) name)))
If you want the function
to return something else based on the actual value of :a, you could check if it is a function and invoke it with name, otherwise return the value.
(defn the-function
[name field]
(when-let [v (field :a)]
(or (when (fn? v)
(v name))
v)))
Reading from your own answer now I am even more confused what actual problem you are trying to solve, but I hope that this answer could provide help.
Upvotes: 0
Reputation: 1028
So this is how I solved this problem after the comments of A. Webb and Jeremy Heiler.
The initial constructor was changed to this:
(defn cons-field [b]
{:a nil ; either delete completely or comment that
; the case will be handled by the caller
:flag xx ; true or :case-qualifier
:b b
:c "default"})
The calling func was changed to this:
(defn the-function [name field]
(let [case-q (:flag field)]
(cond
(= case-q :case-qualifier) (get-name name) ; you can have many different cases
; conciser using constants for these qualifiers
(...) ()))) ; else as normal
Then the logic initially put in the map goes in a different func:
(defn get-name [name] (str name "!"))
Hope this helps someone else :)
Upvotes: 0