Fork me on GitHub
#beginners
<
2018-04-08
>
brnbraga9500:04:26

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"))

platypusdiamond00:04:51

(get "id") should work

brnbraga9500:04:42

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.

brnbraga9500:04:33

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.

brnbraga9500:04:45

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

mfikes00:04:03

@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"))

mfikes00:04:13

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

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

brnbraga9500:04:06

awesome, right what I needed.

seancorfield01:04:34

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

(-> x
    (first)
    (:id))

seancorfield01:04:05

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

seancorfield01:04:56

@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")))

mfikes01:04:52

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

seancorfield02:04:57

(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)

mfikes02:04:54

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

seancorfield02:04:11

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.

seancorfield02:04:47

Prefer the use of keywords for hash keys.

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

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

seancorfield02:04:04

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)

seancorfield02:04:36

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

shakdwipeea08:04:09

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/datahike

bronsa08:04:39

@shakdwipeea

`(foo ~'bar)

bronsa08:04:54

bar wont gt namespace qualified when you do ~'

shakdwipeea08:04:33

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

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

bronsa08:04:27

FYI clojure.tools.reader exposes syntax-quote as a function

bronsa08:04:41

and a pluggable resolve-symbol

bronsa08:04:08

so you can do e.g.

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

bronsa08:04:40

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

daniel.gomme13:04:50

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))

daniel.gomme13:04:26

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]
           [email protected](map (fn [op & args] `(~op ~one ~two [email protected])) 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

daniel.gomme13:04:16

I'm also very new to writing macros

moxaj15:04:54

@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

moxaj15:04:02

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

daniel.gomme15:04:49

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

daniel.gomme15:04:16

(will take a sec to think of an example)

moxaj15:04:42

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

daniel.gomme15:04:46

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

daniel.gomme15:04:19

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}]

daniel.gomme15:04:03

so that macro would roughly expand to

daniel.gomme15:04:58

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

daniel.gomme15:04:12

and that trend would continue with more add-ops added

moxaj15:04:31

(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}]]

moxaj15:04:44

would something like that suffice? no macro needed

moxaj15:04:02

assuming you have control over add-op

daniel.gomme15:04:17

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

daniel.gomme15:04:29

And I think that'd work, yeah

daniel.gomme15:04:46

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

daniel.gomme15:04:26

(already super thankful though, needed that fresh perspective)

moxaj15:04:04

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

moxaj15:04:25

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

daniel.gomme15:04:40

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

moxaj15:04:52

at first glance, you probably need [email protected] and that's it

daniel.gomme15:04:15

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

jeroen.leenarts19:04:53

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?

rakyi20:04:48

in clujure mode type ,'

rakyi20:04:56

or even SPC SPC clojure-jack-in anywhere

rakyi20:04:26

@jeroen.leenarts

loganpowell20: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 (Reflector.java:80)
What am I missing 😞

schmee20:04:20

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

schmee20:04:53

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

schmee20:04:41

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

loganpowell20:04:59

@schmee! Let me check πŸ‘‹:

noisesmith20:04:22

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

loganpowell20:04:56

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

loganpowell20:04:15

my first attempt looked really hairy

noisesmith20:04:22

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

noisesmith20:04:52

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

loganpowell20:04:53

is the redefine redundant? I was just following a tutorial

noisesmith20:04:33

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

loganpowell20:04:50

same arity and argument types?

loganpowell20:04:02

I'm completely new to clojure

noisesmith20:04:37

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

loganpowell20:04:05

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

loganpowell20:04:13

so I can just use the defaults?

noisesmith20:04:19

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

noisesmith20:04:45

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

loganpowell20:04:53

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

loganpowell20:04:21

yep, regular reduce and filter worked drop-in

loganpowell20:04:30

Thank you for letting me know!

loganpowell21:04:24

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

javazquez22:04:53

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 πŸ™‚

javazquez22:04:55

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

mfikes22:04:49

@javazquez It might be worth trying

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

javazquez23:04:05

Thanks! @mfikes That worked

javazquez23:04:30

never would have thought to go that route

mfikes23:04:49

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")

mfikes23:04:38

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

javazquez23:04:57

I will definitely take a look at cljs-oops

mfikes23:04:37

@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.

mfikes23:04:40

(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.)

javazquez23:04:15

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

javazquez23:04:05

I am doing it to target aws lambda

mfikes23:04:28

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.