This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-04-08
Channels
- # beginners (92)
- # boot (2)
- # cljsrn (6)
- # clojure (33)
- # clojure-austin (1)
- # clojure-dev (18)
- # clojure-spec (7)
- # clojure-uk (2)
- # clojurescript (35)
- # cursive (13)
- # data-science (3)
- # datomic (1)
- # defnpodcast (1)
- # figwheel (1)
- # fulcro (27)
- # instaparse (1)
- # java (2)
- # leiningen (8)
- # off-topic (5)
- # onyx (1)
- # portkey (2)
- # re-frame (9)
- # reagent (2)
- # ring-swagger (1)
- # shadow-cljs (136)
- # test-check (3)
- # tools-deps (29)
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
(first))
-> {"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
(first)
("id"))
so I was thinking of something like:
(-> x
(first)
(reference "id"))
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"))
You could also use reference
as the symbol, I suppose.
(as-> x reference
(first reference)
(reference "id"))
awesome, right what I needed.
@brnbraga95 If your keys were keywords, instead of strings, you could do
(-> x
(first)
(:id))
(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
(first)
(as-> ref (ref "id")))
(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
)
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.
https://github.com/bbatsov/clojure-style-guide#collections has several things to say...
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)
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. https://github.com/replikativ/datahikeAh.. thanks, that works. the query now looks like
(defn query [conn query]
(h/q `[:find (~'pull ~'?e ~query)
:where [~'?e ::title]] @conn))
so you can do e.g.
(defn s [f]
(binding [t.r/resolve-symbol identity]
(t.r/syntax-quote f)))
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 itI'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)
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-op
s 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}]]
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)
Yeah, I think I'll check out the source for it. Thanks!
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?
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 (Reflector.java:80)
What am I missing π@loganpowell try printing the value of %
in your reduce method and make sure it is what you expect
@schmee! Let me check π:
@loganpowell: what's your motivation for redefining reduce and filter?
@schmee it seems to be working in isolation @noisesmith I'm mostly following some exercises, but trying to use higher order functions
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
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)
same arity and argument types?
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
is there a way to clean/clear the namespace in the repl?
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)
yeah, it's probably wrong on many levels π
yep, regular reduce and filter worked drop-in
Thank you for letting me know!
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"])
More info on externs is here https://clojurescript.org/reference/advanced-compilation
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")
@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.