Fork me on GitHub
#beginners
<
2019-05-02
>
Charlot00:05:44

Hello. I'm trying to write some generative tests with spec, and am unsure about how to refer to namespaced keywords from another namespace

noisesmith00:05:25

you can spell out the full namespace name, or use ::alias/foo if it's required with an alias

Charlot00:05:19

so ::project.core/graph for :: graph in project.core?

noisesmith00:05:37

no, :project.core/graph

noisesmith00:05:52

but if you had required [project.core :as project] you could do ::project/graph

Charlot00:05:11

Okay, i tried that, but something must be screwy at the repl. Trying it directly works. Thanks

noisesmith00:05:49

(cmd)bar=> (= ::foo :bar/foo)
true
(cmd)bar=> (ns baz (:require [bar :as b]))
nil
(cmd)baz=> (= ::b/foo :bar/foo)
true

Charlot00:05:06

got it working now.

Charlot00:05:26

one question. How long does stest/check usually run for? Trying to figure out if this is hanging or just slow

Joel03:05:21

(some #{:c} '( :a :c :d)) works however, (map #(some #{:c}) '(( :a :c :d ) ( :e :f ))) does not???

lilactown03:05:24

@joel380 #(some #{:c}) executes some without the argument passed into the function

lilactown03:05:46

you want to use the argument like so: #(some #{:c} %)

👍 4
Joel03:05:38

is there any defacto standard library for processing json?

seancorfield03:05:29

And if you "outgrow" that one, take a look at https://github.com/dakrone/cheshire

💯 4
ikitommi04:05:12

@kbosompem if you could build a small repo with your setup or reitit + war, I could try to check it out. I think the ServletContext is not handled (at least with swagger paths).

acim104:05:16

Emacs question...I DON'T want to stop at hyphen when I'm doing word navigation like M-f, I just want to treat it as another letter in the word and be "blind" to it. I have this in the bottom of my init.el file: (modify-syntax-entry ?- "w") ...it's doing nothing. Kind of driving my nuts. Any ideas?

acim104:05:47

In other words, if point is before abc-def-ghi...we're stopping at every frigging dash as we go..with my current impotent solution (sorry, it's late, I'm annoyed...I should probably take a bubble bath and drink some herbal tea or something...)

acim104:05:51

Found a solution..of sorts. 1. Use superword-mode: https://emacsredux.com/blog/2014/08/27/a-peek-at-emacs-24-dot-4-superword-mode/ 2. Comment out the borrowed clojure init script I was using which was undermining it by setting subword-mode...

skykanin10:05:45

I'm building a POST request handler using compojure and ring. How do I deencode json objects that are sent in the POST req through body? #object[org.eclipse.jetty.server.HttpInputOverHTTP 0x77757eb2 HttpInputOverHTTP@77757eb2[c=0,q=0,[0]=null,s=STREAM]] is there middleware for this? SOLVED: Found the wrap-json-body middleware

ok11:05:19

how to trim string to first 10 symbols?

jaihindhreddy11:05:04

BTW, Strings have characters in them, not symbols.

jaihindhreddy11:05:21

clojure.lang.Symbol is a different data type.

ok11:05:28

string is ok 😉

dharrigan11:05:47

(subs "hello world" 0 9)

👍 4
jumar11:05:38

be aware that this fails if your string is shorter than 10 chars or nil

dharrigan12:05:47

Good point, perhaps a check might help?

dharrigan12:05:50

(if (and (seq foo) (> (count foo) 4)) (subs foo 0 5) foo)

dharrigan12:05:02

where foo is the string

nmkip12:05:06

Hi! Quick question (or not): what do you use for testing? I know there's something called Midje haven't tried it yet, still very new to clojure 😄

jumar12:05:43

clojure.test. No need to go fancy especially when you are a beginner.

nmkip12:05:40

That one?

nmkip12:05:37

Thanks. I'll give it a try

seancorfield12:05:59

My first ever screencast https://youtu.be/ZhzMoEz4j1k showing my workflow with Atom, Chlorine, and REBL.

💯 16
seancorfield12:05:06

(Since folks have asked me about my REBL-based workflow)

👍 28
Ivan Koz13:05:08

Sean please do more, your voice and narration style are very nice and chill.

Kari Marttila13:05:01

Very informative and well-done presentation. Your voice is really easy to follow. This is exactly what I have been looking for: Clojure gurus telling how they work with Clojure tools and explaining their workflow.

🙏 4
Kari Marttila14:05:46

Thanks a lot Sean. I realized that those kind of presentations are very important for lone wolves like me who mostly learn Clojure without a Clojure mentor (i.e. e.g. at work some senior Clojure developer helping). Watching those kind of presentations and asking Clojure gurus here at slack are pretty much the only ways to learn effective Clojure workflows real Clojure professionals are using.

seancorfield15:05:55

Thank you both for the compliments! It took a lot of "prodding" to make this one. It will probably take less prodding in future.

Kari Marttila16:05:55

I definitely have to try REBL in my hobby project. And if I find a new Clojure/AWS project in my corporate life I'll immediately buy a license for work (one developer license just $36/year if I understood correctly).

seancorfield17:05:22

Yup, $3/month paid to Patreon for Cognitect.

seancorfield17:05:03

Since I already have $35-40/month going to various writers and artists on Patreon, that was a no-brainer for me to add Cognitect as well.

jaide19:05:56

Awesome video Sean. I learned a lot.

dharrigan13:05:16

Very nice. What's the alias for launching rebl and a socket server at the same time?

seancorfield15:05:28

I realized afterward I should have a) left the command on-screen longer while describing it (before pressing enter) and b) shown my .clojure/deps.edn file!

dharrigan13:05:24

Found it 🙂

Ivan Koz13:05:47

is there an automated way to check lein plugins for new versions?

jumar14:05:42

lein-ancient should work with :plugins - see https://github.com/xsc/lein-ancient#check-artifacts

👍 4
solf16:05:08

I'm reading through clojure transients doc and the meaning of a phrase escapes me: > Note in particular that transients are not designed to be bashed in-place.

solf16:05:19

What means to "bash in place" in this context?

hiredman16:05:00

they are not a mutable datastructure so don't use them like one

noisesmith16:05:46

it means use the return value of the update operations

solf16:05:06

oh okay, so it means (assoc! transient-vector ...) doesn't modify transient-vector (or at least not in any expected way)

hiredman16:05:05

transients are best understood in the context of https://blogs.oracle.com/jrose/larval-objects-in-the-vm

noisesmith16:05:11

(cmd)baz=> (def h (transient {}))
#'baz/h
(cmd)baz=> (dotimes [i 10000] (assoc! h i (inc i)))
nil
(ins)baz=> (persistent! h)
{0 1, 1 2, 2 3, 3 4, 4 5, 5 6, 6 7, 7 8}
- it does until it doesn't

noisesmith16:05:36

the first 8 updates were in fact bashed in place successfully, but the rest are lost

Alex Miller (Clojure team)16:05:06

but all of those words are implementation details subject to change

Alex Miller (Clojure team)16:05:20

the bottom line - use the same calling pattern you'd use for persistent ops

dkrieger16:05:27

is it possible to get the passed symbol name inside defmacro?

(def zip "hello")
(def zap "world")
(defmacro foo [bar baz] `(str *bar baz)) ; '*' is a made up hypothetical symbol
(foo zip zap)
;;=>"zipworld"

solf16:05:36

interesting, thanks for the explanations

hiredman16:05:20

but, I feel like I should also say, it is highly unlikely you will ever actual use transients(in any kind of say web app logic), so as this is #beginners I might even suggest just ignoring them

solf16:05:03

Actually my first side project will be a simple browser game in clojurescript, so I'll probably use transients soon enough

hiredman16:05:58

I don't write clojurescript, but I kind of doubt they will make any kind of useful difference for you

hiredman16:05:22

in very sweeping terms, transients are a micro optimization, most performance problems are macro (the wrong algorithm choice vs. inefficient implementation of the algorithm)

noisesmith16:05:35

@dougkrieger I think in this case you might want `?

Alex Miller (Clojure team)16:05:35

when evaluating a macro, &form is bound to the form being expanded

noisesmith16:05:08

if you want to resolve bar to zip inside your output, use ` to quote, and ~ to selectively unquote

Alex Miller (Clojure team)16:05:23

oh, maybe I'm trying to answer a fancier question than you asked :)

😄 4
Alex Miller (Clojure team)16:05:40

args to a macro have not yet been evaluated

noisesmith16:05:42

not only that, but his form doesn't use the args at all

dkrieger16:05:59

ah ok, switching to `

hiredman16:05:25

clojure uses transients inside a number of functions in clojure.core to speed them up as they are used very often, but I don't think I have ever taken code I've written and added transients to make it go faster

4
Alex Miller (Clojure team)16:05:25

any time you are filling a large collection in a constrained scope (for example, by reading data from a file or db) is a good place to take advantage of this

Alex Miller (Clojure team)16:05:45

that said, if you are using something like into, it will do it for you

dkrieger16:05:46

hmm... I think I do need &form? I don't want the value of the args, just arg's symbol, e.g. as a string

Alex Miller (Clojure team)16:05:33

can you give an example of what you want to happen?

noisesmith16:05:05

to get the arg's symbol you use ~(str x) ~but that isn't what you were doing in your example at all~

noisesmith16:05:21

your example could have been a function actually OK I see I misread now, you want ~(str x)

hiredman16:05:58

not even that

hiredman16:05:28

he is passing in a symbol to his macro and wants the value of the var named by that symbol in the output

noisesmith16:05:10

(ins)baz=> (def zap "world")
#'baz/zap
(ins)baz=> (defmacro foo [bar baz] `(str ~(str bar) ~baz))
#'baz/foo
(ins)baz=> (foo zip zap)
"zipworld"

dkrieger16:05:12

i want a macro that I can call like (foo zip zap) and e.g. get "zipworld"; the value of zap would have been "world" at the time, and the value of foo i don't care about, I care that the variable is called foo

hiredman16:05:32

my suggestion is don't write a macro

dkrieger16:05:34

@hiredman I don't think that's accurate

dkrieger16:05:49

I don't want the value of the variable. I want the name of the variable when the macro is called

noisesmith16:05:52

@dougkrieger my macro above does exactly what you describe

dkrieger16:05:49

@noisesmith yes that looks exactly right, trying it out now, thanks!

dkrieger16:05:21

I think I fundamentally misunderstood what's going on wrt defining macros

dkrieger16:05:42

hmm, I get this

(def zip "hello")
(def zap "world")
(defmacro foo [bar baz] `(str ~(str bar) ~baz))
(foo zip zap)
;; => (cljs.core/str "" nil)
shouldn't matter that I'm using clojurescript, right?

lilactown16:05:59

you can't write macros in a CLJS REPL

Alex Miller (Clojure team)16:05:03

macros are functions from code to code

noisesmith16:05:12

a macro only works in cljs if it's from a different compilation unit right?

noisesmith16:05:34

otherwise it acts like a function (a broken one)

dkrieger16:05:24

so If I move it to a different subdir in /src and require it, it should work then?

noisesmith16:05:07

if it gets required from another file, I think so - cljs no longer uses require-macros right?

dkrieger16:05:44

hmm, it seems like require-macros is a way to get macros from a clj file. I got the same results when just moving it to my lib file

noisesmith16:05:56

:thumbsup: - I haven't used cljs in over a year so forgot some details

dkrieger17:05:12

trying out the steps in this to no avail: http://blog.fikesfarm.com/posts/2015-09-07-messing-with-macros-at-the-repl.html suggests putting the macro in (ns actualns.core$macros)

hiredman17:05:57

that post is almost certainly using the self hosted fork/build of clojurescript which is, I gather, another thing entirely

hiredman17:05:41

or maybe it isn't? maybe he uses bootstrapped clojurescript to refer to the official clojurescript?

dkrieger17:05:40

yeah, figwheel-main docs point to writing macros in clojure

dkrieger17:05:16

writing a simple library to get a better feel for clojure(script), probably won't be of interest to anyone but it's helping find gaps in my understanding

dkrieger17:05:31

basically a wrapper around garden

hiredman17:05:07

mfikes is the main dev for a self hosted cljs project I believe

lilactown17:05:18

@dougkrieger you're using figwheel right?

lilactown17:05:06

if you create a new file my_ns/core.clj, write your macro and then (:require-macros [my-ns.core]) in your ns declaration, it should compile and include those macros

noisesmith17:05:49

oh so require-macros is still a thing OK

dkrieger17:05:56

confirmed on both counts

dkrieger17:05:11

I wonder what this means in terms of portability. I vaguely remember seeing something where figwheel can share code between clojure/clojurescript (e.g. using my cljs functions inside the clj macro definition). time to tinker some more

noisesmith17:05:09

the symbols output by the body of your macro don't need to exist, in your cljs code they successfully expand to something that exists when you are compiling so it happens to work

noisesmith17:05:33

as puredanger says, a macro takes a code form and returns a new code form, it can do anything it likes in the middle

dkrieger17:05:29

right! clearly I need to get more familiar with macros

noisesmith17:05:59

it can be helpful to look at a simple macro and do the exercise of expanding by hand, seeing what gets done at each step as if it were an algebra problem

noisesmith17:05:54

at this step we are constructing the symbols / values to go in the form, then the next step compiles the resulting form but only if there are no more macros to simplify etc.

dkrieger17:05:41

when you have `(~(foo bar)) , bar is still quoted, right? so if I wanted bar to evaluate, I would need to say...

dkrieger17:05:48

`((foo bar))

noisesmith17:05:57

no, bar is fully evaluated in your original

noisesmith17:05:15

(and passed to foo)

noisesmith17:05:01

@dougkrieger macros don't have quoting rules, ` does

dkrieger17:05:02

then I'm confused how

(defmacro foo [bar baz] `(str ~(str bar) ~baz))
(foo zip zap)
;; => "zipworld"
worked

noisesmith17:05:14

because when foo runs bar is a symbol

dkrieger17:05:33

ah! it just clicked

noisesmith17:05:38

adding an extra ~ won't give you what bar points at, that probably doesn't exist yet when the macro is expanding

noisesmith17:05:56

if you need what bar points at, emit bar and let the compiler make code that resolves it

dkrieger17:05:15

str makes it eval to itself

noisesmith17:05:31

this becomes especially important when you discover what you can't do in macros because the thing you want to transform can't exist yet

dkrieger17:05:32

i.e. when the expanded macro is evalled

noisesmith17:05:18

it evaluates while it's a symbol in the macro, and because it evaluates then, it doesn't get resolved in the compiled code, the string literal you generated is used as is

dkrieger17:05:55

do you have a patreon? you've really been helping me out a lot over the past few days, I appreciate it

noisesmith17:05:11

Don't worry about it, thanks for the kind words

🍻 4
dkrieger17:05:23

right. makes sense now. I understand a lot of the high level rules/principles of macros, but still developing an intuition for the consequences of those rules. thanks for helping out

skykanin17:05:13

I'm trying to update a map called graphnetcoll, which has a key called collection which points to a vector, but I'm getting this error Error printing return value (IllegalArgumentException) at clojure.lang.RT/seqFrom (RT.java:553). Don't know how to create ISeq from: clojure.core$constantly$fn__5657

noisesmith18:05:07

update passes your function as the first arg (here that's map) then the original value, then the other args you supply

noisesmith18:05:23

so you end up with (map some-vector (constantly "test"))

noisesmith18:05:41

you can't map anything across a function constructed by constantly, and that's how we get this error

noisesmith18:05:16

you must have some other layer running here if the error is "Error printing return value ..."

noisesmith18:05:08

you can fix this by using an anonymous function eg. (update foo :k #(map %2 %1) (constantly "test"))

noisesmith18:05:07

perhaps nicer: (update foo :k (partial map (constantly "test")))

juan_17:05:14

Is there an alternative to (read-line) where text isn’t in clear text

noisesmith18:05:27

for this I think you need OS specific terminal control (if you mean being able to turn off echo in the terminal to type in sensitive info)

juan_18:05:42

Will read documentation on Lanterna

noisesmith18:05:36

of course this only applies if you expect your user to interact with the clojure program in a terminal - for other contexts you would need to find out what is offered by that API

juan_18:05:45

I’ll test “stty -echo” then running read-line

noisesmith18:05:43

you could use clojure.java.shell/sh to run that (shell/sh "stty" "-echo")

noisesmith18:05:03

if that's all you need - lanterna might be more portable though(?)

juan_18:05:07

I’ll try lantern route first

dkrieger18:05:14

is there some equivalent to partial application for macros?

lilactown18:05:58

what do you mean

noisesmith18:05:34

partial application is just syntax sugar for generating a new function

noisesmith18:05:48

in a macro you can easily generate a function, or a new macro...

Ivan Koz18:05:37

sorry was wrong

dkrieger18:05:34

so there can be a generalized macro mpartial that can take any macro as arg1, then return a macro w/ arg2-argN effectively "pre-populated" -- essentially, the same signature and functionality as partial has for functions?

hiredman18:05:39

you can't partially apply macros, it doesn't make sense to do so

hiredman18:05:13

a macro cannot return a value

hiredman18:05:18

a macro returns code

dkrieger18:05:41

so lets assume it returns a form that is a function call

hiredman18:05:59

and you cannot pass around the result of a macro, because to pass it around you are running code at runtime, and macros run at compile time

dkrieger18:05:42

I guess what I want is to have a "higher order macro" that lets me effectively pre-populate parts of that function call, but in a generalized way. I don't see a way it could be possible, but I'm still learning macro usage

hiredman18:05:15

when writing macros, the best thing to do is first figure out how to do what you want without macros

hiredman18:05:02

if you cannot figure out how to do it without macros then it is very unlikely to work

hiredman18:05:30

because a macro is a transform, from some source to target

dkrieger18:05:45

I sorta did that, but replaced the public function w/ a macro so I could add some extra functionality. I was able to add said functionality, but now I have to rework where I was using partial application to simplify calls

hiredman18:05:48

if you cannot figure out the target, you are not going to be able to write a macro

hiredman18:05:31

whatever you are doing almost certainly doesn't require a macro and would be better off done without a macro

dkrieger18:05:00

I need a symbol name at runtime, so I fundamentally need a macro -- right?

noisesmith18:05:19

for that usage pattern, a macro that expands to an anonymous function would provide enough indirection to preserve that sort of usage pattern - but you'd have to decide which args become macro args, then pass the rest to the function the macro expanded to

hiredman18:05:21

not sure what you mean

hiredman18:05:27

a symbol is a valid value

noisesmith18:05:49

they want to capture some symbols from the invocation form, aside from the value they resolve to

hiredman18:05:10

why do you want to capture some symbols?

dkrieger18:05:38

I'm using reagent and I want to derive classes from component function names

dkrieger18:05:57

and I've got it working, but it ruined the interface a little bit because I can no longer use partial

dkrieger18:05:50

I think I could just bury the macro call into a function definition instead of invoking my original function via the macro and restore the ability to use partial application to streamline the interface. maybe

noisesmith18:05:21

the macro call needs to be outside the function definition to see the arg symbols from the call site

noisesmith18:05:43

but it could create a function that could then be partially applied

dkrieger18:05:59

true. better put, what I think I need to do is first get the new function resulting from partial application, then use the macro to call that

hiredman18:05:00

that is unlikely to work

hiredman18:05:17

assuming a class is something that needs to be statically known

hiredman18:05:30

which is the only reason you would need to generate it from a macro

hiredman18:05:42

you cannot do that

hiredman18:05:57

the function and the partial application are runtime values

hiredman18:05:07

macros are compile time

dkrieger18:05:21

yeah but the evaluation of the macro's expansion is at runtime, so what I described would work

dkrieger18:05:51

I'm not calling the function in question in the macro, the macro expands to a form that looks like a call to that function

noisesmith18:05:17

but to do partial application you need to instead expand to a function literal that would do that call

noisesmith18:05:38

you could still partially apply (just not for the args that are needed for the class name)

dkrieger18:05:55

the partially applied arg is not needed for the class name

noisesmith18:05:33

(defmacro with-class [sym] `(fn [x# y# z#] ...))
- you can partially apply the function the macro creates

noisesmith18:05:47

and also use the value sym points to inside the function

hiredman18:05:08

it does look like maybe you can create classes purely at runtime

dkrieger18:05:45

I think if I switch `(from this form) (list 'to this form), I can partially apply the macro expansion target before I call the macro and it should work. I noticed the second form resolves to to a different namespace than the first form resolves from, but I don't understand why

dkrieger18:05:31

(sorry, botched the second form, now it's better)

noisesmith18:05:45

'to is a symbol, which in a form like that acts as a function that looks itself up in something associative

dkrieger18:05:32

so it will use the namespace of the call site of the macro, whereas `(from) uses the definition site -- at least that's what it seems to be doing

noisesmith18:05:31

to avoid auto-namespacing (eg. to use something that isn't visible at macro definition time),

`(~'foo ...)

noisesmith18:05:32

there's a lot of gotchas to writing macros like that though

dkrieger18:05:31

yeah, I'm going to see if returning expanding to a thunk as you suggested can work for this use case

Ivan Koz18:05:25

@dougkrieger apparently you can partially apply to a macro i'm sure it's not useful

(defmacro my-when
  [test & body]
  (list 'if test (cons 'do body)))

(def my-when2 (fn [&form &env & body] (my-when &form &env true body)))

(. (var my-when2) (setMacro))

dkrieger19:05:53

I wonder if that would work in clojurescript though^ probably not

Ivan Koz19:05:21

never used cljs

skykanin19:05:37

is there a way to convert json fields in a psql table to clojure datastructures through the jdbc wrapper?

Ivan Koz19:05:47

@nicholas.jaunsen not sure about jdbc but converting json to clojure can be done with https://github.com/dakrone/cheshire

skykanin19:05:29

I'm dumb, it is converting it back to clojure data, but all the keys are strings 😐

seancorfield19:05:18

Oh, you already have it working. clojure.walk/keywordize-keys is probably your friend at that point 🙂

skykanin19:05:37

I didn't do anything lol, when I query a JSONB field from psql it converts it to clojure data, I'm guessing that's the psql driver, but keywordize was exactly what I needed thanks!

dkrieger19:05:09

would any of this be idiomatic, and if not, what might be a better pattern? in a library...

;; pattern 1
(ns mylib)
(def metadata (atom nil))
(defn- foo [useful] {:meta @metadata :data useful})

;; pattern 2
(defn bar [pure but repetitive #_followed-by useful] {:meta {:first pure :middle but :last repetitive} :data useful}) 
(defn partial-apply-bar [& args ] (def- bar-conventient (apply partial bar args))) ; this is certainly an ugly public function, maybe it could be a private function called by a watcher on the `metadata` atom?

;; macros
(defmacro foo-mac [useful] `(foo {:name ~(str  useful) :value ~useful})
(defmacro bar-mac [useful] `(bar-convenient {:name ~(str  useful) :value ~useful})
in calling code...
(def cherry "favorite shade of red")

(reset! mylib/metadata {:first "John" :middle "H" :last "Smith"})
(mylib/foo-mac cherry)

(mylib/partial-apply-bar "John" "H" "Smith")
(mylib/bar-mac cherry)

dkrieger19:05:49

if the macros weren't necessary, I'd just have the calling code do partial application to avoid introducing state into the library

noisesmith20:05:30

@dougkrieger shouldn't ~(useful) just be ~useful or else you'd end up calling a string?

dkrieger20:05:25

correct, I'll fix that, woops