Fork me on GitHub

hey guys, when I am using threading_macros can I have a the referente of the current value inside a function ? right now I have the following value, lets call ia x:

({"id" "c0033410-981c-428a-954a-35dec05ef1d2", "type" "bills-questions", "urgent" true})
I want to get the id value by using threading macros, the following cold gives me the following:
(-> x
-> {"id" "c0033410-981c-428a-954a-35dec05ef1d2", "type" "bills-questions", "urgent" true} I know that I can get the id value as follows: ({"id" "c0033410-981c-428a-954a-35dec05ef1d2", "type" "bills-questions", "urgent" true} "id) but how can I implement this last function inside the macro? ps the following does not work.
(-> x
so I was thinking of something like:
(-> x
    (reference "id"))


(get "id") should work


yeah, I know there are some workarounds, but I often see myself in situation where I want to use macros but I cant, because I need the current state, lets say for example I want to send the current value to a function as the secoond parameter for example.


I am saying this because I heard that I am using too much let on my code, and that idiomatic clojure tents to use macros instead.


so I am just trying to know situation where I need lets


@brnbraga95 If you use as-> it lets you define a symbol to refer to the "current state" as I think you are defining it. In your example, with $ being the symbol representing the "current state" then

(as-> x $ 
  (first $) 
  ($ "id"))

πŸ‘ 4

You could also use reference as the symbol, I suppose.

(as-> x reference
    (first reference)
    (reference "id"))

πŸ‘ 4

awesome, right what I needed.


@brnbraga95 If your keys were keywords, instead of strings, you could do

(-> x


(one of the reasons it's easier to work with hash maps that have keywords as strings)


@mfikes Probably fair to point out, since this is the beginners channels, that as-> is really intended to be used inside -> or other thread-first macro:

(-> x
    (as-> ref (ref "id")))


Oh, I suppose I'm a beginner also. TIL. πŸ™‚


(that's why it has the slightly strange signature of [expr name & forms] whereas everywhere else in Clojure there's a binding, it's name expr)


Yep. I always get it backwards, but now I see why. πŸ™‚


As for accessing maps that have string keys, I'd probably lean to (get my-map "the-key") rather than (my-map "the-key") because I think it does a more explicit job of calling out the fact that "it's not a keyword"... But I'd have to look at the style guide to see what's recommended as idiomatic there.


Prefer the use of keywords for hash keys.

;; good
{:name "Bruce" :age 30}

;; bad
{"name" "Bruce" "age" 30}


Prefer the use of keywords as functions for retrieving values from maps, where applicable.

(def m {:name "Bruce" :age 30})

;; good
(:name m)

;; more verbose than necessary
(get m :name)

;; bad - susceptible to NullPointerException
(m :name)

πŸ‘ 8

So that suggests (get m the-key) is probably better when you're not using keywords?


Is it possible to syntax-quote forms without symbols being namespace qualified ? I have this function

(defn query [conn query]
  (h/q  '[:find (pull ?e query)
            :where [?e ::title]] @conn))

(query conn [::title])
Here I need to evaluate query, but if I use syntax-quote then pull and ?e become namespace qualified which is causing some problems. FWIW I am using datahike for the query.


bar wont gt namespace qualified when you do ~'


Ah.. thanks, that works. the query now looks like

(defn query [conn query]
  (h/q `[:find (~'pull ~'?e ~query)
         :where [~'?e ::title]] @conn))


FYI exposes syntax-quote as a function


and a pluggable resolve-symbol


so you can do e.g.

(defn s [f]
  (binding [t.r/resolve-symbol identity]
    (t.r/syntax-quote f)))


and just (s '[foo ~bar baz])

πŸ‘ 4

I have a major pain of a macro question. So, I'm attempting to do a version of as-> that kind of inserts two items, something like

(my-macro '(acc accs)
   (op 0)
   (op 31))
and it coming out with
(as-> '(acc accs) [one two]
   (op one two 0)
   (op one two 31))


At the moment I have

(defmacro ops2 
  "Thread together several ops and their results"
  [start-vals & ops]
  (let [one   (gensym `one)
         two (gensym `two)] ; Down to here is fine
    `(as-> ~start-vals [~one ~two]
           ~@(map (fn [op & args] `(~op ~one ~two ~@args)) ops))))
but using it gives me a huge garbled amount of errors about failing spec in let, and I have absolutely no clue how to fix it


I'm also very new to writing macros


@daniel.gomme this doesn't make a lot of sense to me; with as->, the value of the previous expression is 'inserted' into the next expression at a given position


can you give an example what your macro would expand to (if it expands to (as -> ..), expand that as well)?


@moxaj the idea is the result of each expression is a list with two items, and both of those items should be inserted respectively


(will take a sec to think of an example)


are those 2 items threaded through somehow, or just the same values?


So if we have (defn add-op [acc ops amount] (list (+ acc amount) (conj ops {:operation :add :amount amount})))


We'd want something like

(ops2 '(0 [])
  (add-op 5)
  (add-op 7))
which would resolve to (13 [{:operation :add :amount 5} {:operation :add :amount 7}]


so that macro would roughly expand to


(let [[acc, ops] (add-op 0 [] 5)]
  (add-op acc ops 7))


and that trend would continue with more add-ops added


(defn add-op [[acc ops] amount]
  [(+ acc amount)
   (conj ops {:operation :add :amount amount})])

(-> [0 []]
    (add-op 10)
    (add-op 20))
;; => [30 [{:amount 10, :operation :add} {:amount 20, :operation :add}]]


would something like that suffice? no macro needed


assuming you have control over add-op


Yeah, I think it might actually make sense to bundle together acc and ops


And I think that'd work, yeah


Am vaguely curious as to the macro version still, as I've bounced off macros so far


(already super thankful though, needed that fresh perspective)


well, your macro is pretty much equivalent to ->, assuming you bundle your args


and if you don't, then check out what -> does, should be a minor modification


Yeah, I think I'll check out the source for it. Thanks!


at first glance, you probably need ~@x and that's it


Gotcha. As is, it's pretty much just me on the project, and it's from the ground up, so options are flexible πŸ™‚


Just out of curiosity. What’s the quickest way to get a REPL going in Spacemacs? I think you need a directory with a clj file in there. Or is there an even more minimal/simple way?


in clujure mode type ,'


or even SPC SPC clojure-jack-in anywhere

Logan Powell20:04:16

When I test the (every?...(reduce... function in isolation it works, but not as part of the larger function. I get this error:

IllegalArgumentException No matching method found: isLetter  clojure.lang.Reflector.invokeMatchingMethod (
What am I missing 😞


@loganpowell try printing the value of % in your reduce method and make sure it is what you expect


ie #(do (println %) (Character/isLetter %))


it is often helpful to print (class %) as well

Logan Powell20:04:59

@schmee! Let me check πŸ‘‹:


@loganpowell: what's your motivation for redefining reduce and filter?

Logan Powell20:04:56

@schmee it seems to be working in isolation @noisesmith I'm mostly following some exercises, but trying to use higher order functions

Logan Powell20:04:15

my first attempt looked really hairy


sure, but reduce and filter are already higher order and come built in


if the point is to build that stuff from scratch, I can offer pointers, but just making sure you're doing it on purpose

Logan Powell20:04:53

is the redefine redundant? I was just following a tutorial


it has the same name as the built in reduce, and behaves almost the same (except slower and less flexible)

Logan Powell20:04:50

same arity and argument types?

Logan Powell20:04:02

I'm completely new to clojure


same argument types, except the built in reduce optionally doesn't need the initial accumulator argument, and is capable of short-circuiting when passed a special reduced value at any processing step

Logan Powell20:04:05

is there a way to clean/clear the namespace in the repl?

Logan Powell20:04:13

so I can just use the defaults?


I bring it up because there's a few things going on in that code, and it could be the redefined reduce and the very weird alternative to filter are just distractions here, I just don't know


you can use ns-unmap, followed by (refer-clojure)

Logan Powell20:04:53

yeah, it's probably wrong on many levels πŸ˜„

Logan Powell20:04:21

yep, regular reduce and filter worked drop-in

Logan Powell20:04:30

Thank you for letting me know!

Logan Powell21:04:24

Thank you @schmee and @noisesmith I got it working with your help.

πŸ‘ 4

I am trying to get clojurescript to pull environment variables from nodejs. I have it working using "lein trampoline noderepl" but when I "lein cljsbuild once min" I get an error when I run: console.log(process.$env$.$DYNAMO_DB_ENDPOINT$); ^ TypeError: Cannot read property '$DYNAMO_DB_ENDPOINT$' of undefined I am pretty sure its the advance closure compiler, just not sure how to resolve. Any help is appreciated πŸ™‚


relevant code (require [cljs.nodejs :as nodejs]) (def endpoint nodejs/process.env.DYNAMO_DB_ENDPOINT) (js/console.log endpoint)


@javazquez It might be worth trying

(goog.object/getValueByKeys nodejs/process #js ["env" "DYNAMO_DB_ENDPOINT"])


Thanks! @mfikes That worked


never would have thought to go that route


More info on externs is here But even if you manage to prevent renaming process, env, etc. you probably wouldn't define an extern for a name of an environment variable. So with externs your code would still end up looking like

(goog.object/get (.-env nodejs/process) "DYNAMO_DB_ENDPOINT")


There is also the cljs-oops lib that is a good alternative to goog.object/get

πŸ‘ 4

I will definitely take a look at cljs-oops


@javazquez It is probably also worth assessing whether your codebase truly gains a benefit by using :advanced with Node. It might be marginal and not worth the additional effort.


(The predominant benefit of :advanced these days is in minimizing code size for web-based targets, and the speed benefits are secondary and not as great as they used to be as JavaScript engines have matured.)


just diving into the clojurescript and nodejs. This is very helpful, Thank you


I am doing it to target aws lambda


Ahh, cool, :advanced certainly helps with launch latency for React Native apps; it may also help with Node in the scenario where you don't want to keep instances running.