Fork me on GitHub
#beginners
<
2019-06-18
>
Eric Ervin00:06:45

I really adored the 1st edition of Dmitri Sotnikov's web development book. As I remember it, it built everything up step by step with Ring and Compojure. Plus the appendix that summarizes Clojure.

tbair00:06:28

Hm. $31 in iBooks... OK.

tbair00:06:51

Let's give Dmitri and Apple some money.

seancorfield00:06:47

Hmm, is Luminus purely server-side? I thought it had a cljs front end in the template? Or do you have to specify more options to get that?

yogthos00:06:40

It uses profile flags for different features, the minimal app will just be Ring + Reitit, and then you can add db, auth, cljs, etc

4
noisesmith00:06:37

I think all the cljs stuff is opt-in with luminus

noisesmith00:06:26

there's many flags documented in the readme, ClojureScript is one of the sections https://github.com/luminus-framework/luminus-template#usage

noisesmith00:06:45

so you tell the template which extra libs / features to include

seancorfield00:06:40

Yeah, just looking at that... one day I'll find the time to go back and learn "modern cljs" tooling and try this out again... last time I touched cljs was 2015 I think.

4
noisesmith00:06:36

my knowledge of all of this is expiring fast as well, just over a year ago I worked full time on an end-to-end cljs->clj webapp, but even that was architected much longer ago, and I haven't had to work with any cljs stuff in the last year

tbair00:06:45

I'm told that React is the mostest awesomest thing out there (and it sure does look like it does some slick stuff), which begat Reagent, etc.

tbair00:06:35

(it can't be any worse than trying to figure out what the hell is going on in The PHP Code That Time Forgot)

noisesmith00:06:58

it's definitely going in the deep end if you are just now learning Clojure as well

tbair00:06:08

(if you know differently, don't disabuse me of that notion, please)

tbair00:06:29

same way the cat learned to swim!

Eric Ervin00:06:39

Don't try to learn a new IDE as well

seancorfield00:06:46

Yeah, back in 2014/2015 at work we built a proof of concept real-time dashboard with Om (which wraps React) and Sente (wrapping WebSockets). Then we re-built it with Reagent (which we preferred). But it was a painful experience and tooling was... basic... Things are much smoother now and there are many more options in the cljs world for framework-y kind of stuff.

tbair00:06:51

Yeah, I'm sticking with vim/fireplace.

tbair00:06:36

A friend/colleague of mine swears by Emacs, but I was much happier when I learned there was vim integration for it.

Eric Ervin00:06:24

Trying to learn Clojure and a new IDE is a classic nOOb blOOper

tbair00:06:15

I should note: I'm not completely at sea, I'm mostly Perl (done a little bit of Catalyst) and I had a course in Lisp in the distant past, so it isn't like I'm completely adrift in a sea of parentheses.

seancorfield00:06:38

I should set aside a weekend when my wife's away to go back and learn/re-learn ClojureScript/shadow-cljs/re-frame I think...

Ahmed Hassan03:06:02

Have a look at Keechma and Hoplon too.

seancorfield03:06:17

Heh, we'll see how far I get ๐Ÿ™‚

Eric Ervin00:06:44

Sounds to me like time better spent playing guitar or Magic the Gathering, but you're deeper into the Clojureverse than I am.

tbair00:06:28

OK, well, thanks folks - got the book, let's see where it goes from here. Thanks for the help!

4
seancorfield00:06:39

@ericcervin I don't play games at all and, despite trying to learn trumpet, keyboards, guitar over the years I am resigned to being completely tone deaf and non-musical ๐Ÿ™‚ Programming is what I've always done for fun ๐Ÿ™‚

๐Ÿ‘ 4
Eric Ervin00:06:57

When we met IRL, you told me about the shirts.

seancorfield00:06:19

Heh, yeah, I have hundreds of T shirts for both concerts and conferences. I was just chatting with my wife yesterday as we were folding laundry and marveling at some of my near-30-year-old Ts that still survive ๐Ÿ™‚

tbair00:06:53

Try the ukulele - it's a lot of fun, and can be learned fairly quickly.

tbair00:06:35

I'm not a string-instrument person, ran into a ukulele teacher in the violin shop where my wife was getting her violin fixed; teacher slammed a uke in my hand, I was hooked.

seancorfield00:06:19

So Amanda Palmer tells me (re: ukulele) ๐Ÿ™‚

yogthos00:06:34

and I can highly recommend shadow-cljs, it makes working with NPM modules completely seamless

yogthos00:06:03

it also works well with tools.deps from what I saw

seancorfield00:06:41

@yogthos If I buy the 3rd ed e-book, is it "complete" as in 2nd ed but just getting rewrites as you work toward the final date? Or does it only containing parts of the book as you (re)write them?

seancorfield00:06:21

My wife leaves for the East Coast on Thursday night (to judge a cat show, back late on Monday), so my weekend will be getting my head around whatever this created:

lein new luminus learning +reitit +jetty +h2 +re-frame +shadow-cljs +auth-jwe
๐Ÿ™‚

yogthos00:06:09

Feel free to ping me about it if you get stuck on anything

seancorfield00:06:32

Thanks. You may regret that offer ๐Ÿ˜‰

yogthos00:06:35

That would indicate that the template or the docs need improvement :)

seancorfield00:06:32

The PDF of your 3rd ed is in my browser for the weekend reading/learning!

yogthos00:06:32

excellent, @U0NLT4Z2Q would be interested in feedback as well

8
tbair00:06:53

hmm... would the "yogthos" mentioned in this book be the same one on this channel?

yogthos00:06:44

Indeed it would :)

tbair00:06:59

Convenient!

tbair00:06:53

... Given that you're skilled at explaining this stuff to folks who are not necessarily used to using it... do you have any insights into my question(s) above? Does any portion of it make sense?

yogthos00:06:48

what were the specific questions again? ๐Ÿ™‚

yogthos00:06:22

3rd ed will be a full rewrite, so chapters are going to appear as me and Scot finish them

seancorfield00:06:08

A conundrum then: do I buy the 2nd ed so I can learn Luminus this weekend, or do I buy the 3rd ed and see how much is available so far? ๐Ÿ™‚

yogthos00:06:42

We've got 5 chapters done so far, so all the basics are there

yogthos00:06:07

Re-frame, as, and swagger are covered

yogthos00:06:48

The next chapters will focus on stuff like auth, file uploads, packaging, testing, and deployment

yogthos00:06:18

So hopefully enough content already to be useful :)

seancorfield00:06:46

For $26, I'll drop the hammer on that then... thanks! Yours is one of the few Clojure books I don't have because I've built lots of Clojure web apps and have been putting off ClojureScript but this seems like a good opportunity to "learn up".

yogthos00:06:03

Awesome, hopefully it lives up to the expectations :)

๐Ÿ‘ 4
johnjelinek01:06:01

(clojure.repl/doc write-secret)
; =>

dal.secretsmanager/write-secret
([secret value])
  write-secret writes a secret
Spec
  args: [:dal.secretsmanager/secret :dal.secretsmanager/value]
  ret: map?
Is there an idiomatic way to figure out what a secret or value contains or how to tell if they're strings or maps or whatever?

ghadi01:06:51

@johnjelinek call doc again on the specs

johnjelinek01:06:00

oic ... so, I need a function that will walk the spec tree if I want to get a good feel for what's all required for my inputs

ghadi02:06:13

You can call spec.gen/sample on it, too

ghadi02:06:31

It will show you randomly generated examples of the map

๐Ÿ‘ 4
gerred02:06:59

is there any up to date guidance on maps vs. records? with extends as metadata and spec, it seems like there's a compelling reason to only use maps or at least heavily prefer them.

ghadi02:06:07

maps 99% of the time

gerred02:06:13

๐Ÿ‘ thanks!

gerred02:06:53

minus the "type participating in protocol" part now I suppose

seancorfield02:06:54

The protocol has to have opted in to metadata use at the definition point.

seancorfield02:06:29

The TL;DR is "use maps" unless you can't, or you have something performance sensitive where you can show records are faster.

seancorfield02:06:41

As an example, in next.jdbc which is heavily protocol-based, only two protocols are extensible via metadata because either it doesn't make sense for the others to be or I specifically do not want folks to do that.

seancorfield02:06:27

If you want to use namespace qualified keys, you also cannot use records.

johnj02:06:33

Is order of insertion guaranteed in maps?

seancorfield02:06:05

@lockdown- hash maps are unordered.

seancorfield02:06:51

If you want a collection that respects "insertion", you should use a list or a vector (depending on how you want to expose the order).

johnj02:06:30

yep, going to use a vector of vectors of map

johnj02:06:18

@seancorfield you mentioned REBL the other day, it only runs in-process, not network support correct?

Cora (she/her)03:06:40

in clojure.spec, how do you specify the values that are allowed in a set?

seancorfield03:06:40

@lockdown- Yeah, in-process. Over-the-wire opens up a giant can of worms ๐Ÿ™‚ Rich talked a bit about it on the #rebl channel early on as something they don't want to tackle (at least, not yet). Serialization & deserialization is hard and there are issues around how to control sequence realization etc.

seancorfield03:06:14

@corasaurus-hex is that a fixed set of values?

Cora (she/her)03:06:23

yes, the cardinal directions

Cora (she/her)03:06:36

#{:north :south :east :west}

seancorfield03:06:51

So you have a set that can contain a subset of that set of values.

Cora (she/her)03:06:00

some or all, yes

Cora (she/her)03:06:08

0 or more ๐Ÿ™‚

Cora (she/her)03:06:45

I've tried even (s/* keyword?) to no avail

Cora (she/her)03:06:52

I feel like I'm missing something

seancorfield03:06:17

(s/def ::direction #{:north :south :east :west})
(s/def ::dir-set (s/coll-of ::direction :kind set? :min-count 0 :max-count 4))
(fixed min/max, and now fixed :kind)

Cora (she/her)03:06:31

where do you see in the coll-of docs that it can take a type as the first arg?

ghadi03:06:34

kind needs a single :: :

Cora (she/her)03:06:38

it only says it takes a predicate

Cora (she/her)03:06:50

(coll-of pred & opts)

ghadi03:06:10

you have to navigate the docs a bit, it points to a function every IIRC

Cora (she/her)03:06:13

I don't like that I couldn't figure that out from the docs

ghadi03:06:16

which has the options

Cora (she/her)03:06:34

yeah, but it doesn't say the first arg can be a type

Cora (she/her)03:06:39

unless I'm not reading it right

Cora (she/her)03:06:43

which let's be honest is likely

Cora (she/her)03:06:07

::direction in this case

ghadi03:06:28

::direction is the name of a spec

Cora (she/her)03:06:44

ok, I'll use the right word for it next time

Cora (she/her)03:06:01

"takes a pred and validates collection elements against that pred"

Cora (she/her)03:06:10

is "pred" not a predicate function?

seancorfield03:06:18

user=> (s/def ::direction #{:north :south :east :west})
:user/direction
user=> (s/def ::dir-set (s/coll-of ::direction :kind set? :min-count 0 :max-count 4))
:user/dir-set
user=> (s/exercise ::dir-set)
([#{:south} #{:south}] [#{:west :east :north} #{:west :east :north}] [#{:south :east :north} #{:south :east :north}] [#{:south :north} #{:south :north}] [#{:west :south :east} #{:west :south :east}] [#{:south :east :north} #{:south :east :north}] [#{:west} #{:west}] [#{:north} #{:north}] [#{:west :south :east} #{:west :south :east}] [#{:south :north} #{:south :north}])
user=>          

Cora (she/her)03:06:09

I'm missing something

ghadi03:06:13

specs are predicates

Cora (she/her)03:06:24

in the context of the docs?

ghadi03:06:28

sometimes docstrings are a bit terse

Cora (she/her)03:06:45

yeah, seems to be the case here

Cora (she/her)03:06:56

which is usually preferred, once you're familiar enough

Cora (she/her)03:06:11

all the coll-of examples just use a function

Cora (she/her)03:06:37

so I inferred from that that in the docs that pred is just a function

Cora (she/her)03:06:46

but I guess that's what specs are, too

Cora (she/her)03:06:29

is this a special instance where the namespaced keyword causes a lookup for a function?

Cora (she/her)03:06:53

does the macro see a namespaced keyword and know to look up the predicate function in the registry?

ghadi04:06:05

next section in the reference guide covers the registry

ghadi04:06:20

you can register predicates under global (namespace-qualified) names

ghadi04:06:32

and combine them together

Cora (she/her)04:06:19

but I'm wondering why it does the lookup there, in the registry

seancorfield04:06:32

Because that's how specs work ๐Ÿ™‚

Cora (she/her)04:06:43

ok, so it's only a thing in spec

Cora (she/her)04:06:50

and not something I missed about clojure as a whole

Cora (she/her)04:06:05

ok man I'm bad at asking questions some days ๐Ÿ˜œ

ghadi04:06:05

It's a global registry, implemented in the spec library

Cora (she/her)04:06:40

sure, definitely, I just didn't understand the lookup in that position of s/coll-of, I thought maybe I missed some kind of keyword->function resolution rule or something weird

Cora (she/her)04:06:23

I mean I've gone through Getting Clojure and a bunch of exercises and a side project so I'd be pretty surprised if that was the case

Cora (she/her)04:06:30

but I'm new enough that it's entirely possible

ghadi04:06:50

nah you didn't miss that

ghadi04:06:11

it's spec, the library, resolving :qualified/keywords to specs using its registry

Cora (she/her)04:06:22

the predicate section doesn't actually explain that you can do this

ghadi04:06:11

covered in Registry, I think: > A registered spec identifier can be used in place of a spec definition in the operations weโ€™ve seen so far - conform and valid?.

Cora (she/her)04:06:50

that points out that you can use it in those cases

Cora (she/her)04:06:58

and in the docs it calls those arguments specs

ghadi04:06:39

they're generally interchangeable

Cora (she/her)04:06:13

that's the confusing bit, maybe

Cora (she/her)04:06:11

I don't know how to improve the docs, but that bit seems kind of important

Cora (she/her)04:06:14

> (s/spec? keyword?)
;; => nil

Cora (she/her)04:06:54

guess I have some work to do ๐Ÿ™‚

Cora (she/her)04:06:54

I don't know how to make the specific complaint when the solution could be put any number of locations

seancorfield04:06:03

I'd probably advise talking to Alex Miller before you spend too much time on it -- and he's on vacation this week.

4
seancorfield04:06:37

He'd certainly be able to give you guidance on where (or even if) certain sections could reasonably be updated.

ghadi04:06:51

user=> (doc s/spec? ) ------------------------- clojure.spec.alpha/spec? ([x]) returns x if x is a spec object, else logical false

ghadi04:06:05

it's late here ๐Ÿ˜ด

seancorfield04:06:34

"spec object" is something very specific.

Cora (she/her)04:06:42

but I was trying to find a path of inference from "spec" to "pred"

seancorfield04:06:26

The key section is what Ghadi linked you above.

Cora (she/her)04:06:33

I read that section

seancorfield04:06:51

"Any existing Clojure function that takes a single argument and returns a truthy value is a valid predicate spec. ... Here we are passing a predicate which is implicitly converted into a spec. "

Cora (she/her)04:06:53

I guess I'll read it again, because I must be missing something

seancorfield04:06:37

Later on "Note that again valid? implicitly converts the predicate function into a spec. "

Cora (she/her)04:06:56

that's converting it the other direction

Cora (she/her)04:06:54

and on here https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html -- conform and valid have a first arg that's "spec" which is different from the coll-of and friends that take "pred"

seancorfield04:06:58

And in the info box about coll-of: "Both coll-of and map-of will conform all of their elements" -- conform requires a spec

Cora (she/her)04:06:11

implying they're different things

Cora (she/her)04:06:33

add to that that the examples only show them taking symbols that reference functions...

seancorfield04:06:38

The docstrings use pred rather than spec because spec means something specific in much of the library (and this delineation is much more clearly drawn in spec-alpha2).

Cora (she/her)04:06:26

I only mean to point out that this is not at all obvious to the person new to this, that there's a gap in the documentation

Cora (she/her)04:06:37

without a clear line of inference to it

Cora (she/her)04:06:48

it's feedback, not criticism, for the docs

seancorfield04:06:00

Oh, yeah, it's a reasonable criticism that the docs are assuming connections that aren't at all obvious.

Cora (she/her)04:06:22

it's harder to see from the experts side where the gaps are

Cora (she/her)04:06:45

which again, isn't a criticism, just that beginner confusion is a useful tool to seeing where gaps are

seancorfield04:06:28

Part of the problem is that spec is all about composing predicates to create specs -- but there's no good term for the intermediate between a "predicate function" and a "predicate spec" that's nice and short and could be used everywhere that pred is currently used to imply "predicate spec".

Cora (she/her)04:06:45

I went and looked up the function and I can see how it resolves the pred now

seancorfield04:06:50

@corasaurus-hex could you read over the beginning of this section https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha#symbolic-specs and see if it helps clarify the intent?

seancorfield04:06:28

If that helps, it's likely that when spec-alpha2 moves forward, (some of) that language will be lifted up into the spec guide.

Cora (she/her)04:06:59

"symbolic specs and spec objects" are basically where it loses me. I only just learned that there are spec objects at all, and I've never seen s/form used

Cora (she/her)04:06:16

am I using spec-alpha2?

seancorfield04:06:55

No. But that's the "next generation" of spec and that's where things are going.

seancorfield04:06:15

So I was trying to see if the more clearly delineated terminology there helped at all. It seems not.

Mattias10:06:44

Hey, I trying to try out the J9 VM. On Mac, with (now) two jdks installed via Homebrew. Java in a shell runs the JAVA_HOME version just fine, but Iโ€™m not certain lein or Clojure do. Where/how do they find what JDK/JRE to run on?

Mattias10:06:33

Iโ€™m doing the search thing, but there is a lot of information of various quality and uptodateness. ๐Ÿ˜…

alexmiller10:06:29

Clojure 1.10.x should run on any Java 8+

Mattias10:06:55

Does it use JAVA_HOME to find the java version to use?

jumar10:06:21

If using lein you could do lein --version

jumar10:06:35

it should use whatever java is on your path

jumar10:06:12

Btw. I use jenv on Mac OS and while it's not perfect, it is the best thing I've found for managing java installations on Mac

lread11:06:32

hey @U06BE1L6T Iโ€™m finding sdkman works for me https://sdkman.io/ for managing jdks.

jumar11:06:54

haven't tried that - might be good, thanks!

Mattias11:06:04

Iโ€™m trying to get jenv to work, but Iโ€™m not experienced enough. I have installed openjdk12 via brew with both the regular VM and with the openJ9 VM. jenv seems to not manage different installs of the same version... Iโ€™ll keep trying. ๐Ÿ™‚

jumar11:06:10

You probably need to add a new installation via jenv add

jumar11:06:59

Also when you change the installation you often need to start a new terminal window or at least issue the cd command. Finally, export plugin might be useful:

jenv enable-plugin export
(see https://github.com/gcuisinier/jenv/issues/44#issuecomment-233124185)

snurppa12:06:17

Iโ€™m not familiar with jenv, how does it differ from lets say this kind of .zshrc config?

export JAVA_8_HOME=$(/usr/libexec/java_home -v1.8)
export JAVA_11_HOME=$(/usr/libexec/java_home -v11)

alias java8='export JAVA_HOME=$JAVA_8_HOME'
alias java11='export JAVA_HOME=$JAVA_11_HOME'

# default to Java 11
java11

jumar14:06:23

setting JAVA_HOME doesn't mean that you actually have proper java on your path. That is following may return completely different version:

java -version

jumar14:06:47

Many tools respect the JAVA_HOME property but that's not always the case

snurppa09:06:35

Ok I see, thx!

metehan12:06:07

is there any npm type website to find cljs libraries

metehan12:06:55

i am looking for a form validator. so where can i search when I need such stuff

snurppa12:06:18

that has wrappers for JS libraries, iโ€™m not sure if it is of any help if you want pure cljs libraries

metehan12:06:59

yes I know this. I was looking for pure cljs packages

metehan12:06:35

is it ok to use old libraries in cljs

metehan12:06:10

or I should avoid as I would do in JS libraries

mfikes12:06:20

@m373h4n It is ok to use old libraries in ClojureScript. There are rarely breaking changes, if that's your concern.

Cas Shun13:06:36

I'm trying to write a macro which (among other things) expands to the value of a form, and the literal form passed in. So (testmacro (+ 1 2)) would return something like {:val 3 :form (+ 1 2) }. Here's my poor attempt:

(defmacro testmacro [x]
  `(let [form# x
         val# ~x]
     {:val val# :form form#}))
I'm somewhat confused as to why ~x works but x does not here?

ghadi13:06:10

call (macroexpand '(testmacro (+ 1 2)))

Cas Shun13:06:55

@ghadi I have, I get {:val 3, :form user/x}. I'm unsure of how to get what was passed to the macro rather than x when expanded.

Adrian Smith14:06:20

Is there anything out there that would read in SQL strings then outputs honeysql data structures? Would be cool to have for documenting how to use Honeysql

Cora (she/her)14:06:16

https://github.com/jkk/honeysql/issues/182 -- it didn't exist as of late 2017, at least

Andrew14:06:51

@auroraminor x is within syntax quote and therefore resolves to an x that you've previously defined. On my fresh REPL I get this:

user=> (testmacro (+ 1 2))
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: x in this context
To fix the macro you should quote (not syntax quote) unquoted x
(defmacro testmacro [x]
 `(let [form# '~x
        val# ~x]
    {:val val# :form form#}))
I'd write it like this:
(defmacro testmacro [x] `{:val ~x :form '~x})
user=> (testmacro (+ 1 2))
{:val 3, :form (+ 1 2)}

Cas Shun15:06:19

@clojurians971 thanks, that does work.

Cas Shun15:06:01

but, I'm somewhat confused how quote unquote works here. Everything I've read about unquote is that it causes the next sexp to be evaluated, which clearly isn't happening here.

Andrew15:06:55

macroexpand-1 gives you an idea of what's going to be evaluated. Here it is with the version that I wrote because I think the gensyms make the other versions hard to read:

user=> (macroexpand-1 '(testmacro (+ 1 2)))
{:val (+ 1 2), :form (quote (+ 1 2))}
When {:val (+ 1 2), :form (quote (+ 1 2))} is evaluated at the REPL (+ 1 2) turns into 3, and (quote (+ 1 2)) is (+ 1 2). It helps to separate in your mind the stages of things that happen: macro expansion before evaluation of the resulting form at the REPL.

Cas Shun15:06:23

ok, so We can think of the tilde (~) as saying that we bounce outside the surrounding syntax-quoted form and evaluate the following form in that context, inserting the result back where the tilde was. makes sense to me. I've read a few things about ~ and it seems to often be explained as 'causes to be evaluated'

Cas Shun15:06:24

not sure I fully get the difference between macro expansion time and evaluation time yet though

bronsa15:06:05

the phases are (kinda) (eval (macroexpand (read form)))

Suni Masuno15:06:25

Is it possible to have both deeper namespaces and other things within a namespace? Is it a good idea? aka myproj.utils/some-function AND myproj.utils.some-ns/some-other-function

bronsa15:06:39

there's no hierarchical relation in clojure namespaces

bronsa15:06:47

a.b.c is not "part" of a.b

Suni Masuno15:06:47

So the appearance of a hierarchy is convention?

bronsa15:06:46

the filesystem is hierarchical and namespaces follow the filesystem, but that's where it ends

Suni Masuno15:06:23

Neato. ^_^ Now the hard question... is it a good idea? Is that something that is common in the community? Will people be able to brain it?

Cas Shun15:06:26

@bronsa but in my case above, I don't fully understand why '~ is making it an expansion phase value instead of eval.

bronsa15:06:48

@suni_masuno it's perfectly fine

๐Ÿ‘ 4
bronsa15:06:02

@auroraminor think of it as a templating system

bronsa15:06:38

in some other languages you've probably used string interpolation syntaxes like (i'm making it up) "here's my text $(calculate value)"

bronsa15:06:58

it's the same in syntax-quote and unquote (~), just lifted to data

Cas Shun15:06:03

clojure is my first language... almost seeming like a poor choice.

tavistock15:06:45

the number of times i have written a macro is 6 years of clojure is exactly once

tavistock15:06:57

and even then it was probably a mistake

ghadi15:06:13

not a bad first choice @auroraminor

Cas Shun15:06:48

It's been a long journey to even get code working... so many tools

ghadi15:06:24

I would love to hear your merciless feedback about tooling

Cas Shun15:06:50

figuring out what to use, how to edit code, the various types of repls...

Cas Shun15:06:53

it's all extremely confusing.

tavistock15:06:44

have you been using lein templates to get your projects started or doing it by hand?

Cas Shun15:06:05

a lot of the documentation is very poor at describing "This is a problem", "This is a solution", "This is how you use it"

Cas Shun15:06:27

I've been using lein, because that seems to be the best 'push a button' method

tavistock15:06:30

clojure tends to be peoples second or beyond language and the documentation definitely reflects that in places

bronsa15:06:22

definitely don't bother with macros for now then :) they're very rarely used

๐Ÿ’ฏ 4
bronsa15:06:37

clojure has been lots of people's first language, there's a lot of good introductory material out there

Cas Shun15:06:06

yeah, I've read a few books now. Lots of reiteration before anything makes sense

ghadi15:06:24

I would love to hear your merciless feedback about tooling

Suni Masuno15:06:18

@auroraminor as someone who jump around languages professionally let me tell you... they're all like that these days. You get 3 options really 1) tons of tools you have to put together yourself (confusing) 2) one god tool everyone has to use or else, which is short all the features you actually want (powerless) 3) no tools... do everything extremely manually or write it your own dang self (effortful)

Cas Shun15:06:08

I would not mind 3 if there was documentation on how things worked

Cas Shun15:06:21

clojure seems like 1 to me, so far

Suni Masuno15:06:46

Yeah, but you can turn 1 into 3

Suni Masuno15:06:51

java -cp .:<PATH>/clojure-1.6.0.jar:./src clojure.main use ONLY this

Suni Masuno15:06:06

do everything else by hand, and read the language spec. Boom, 1->3

Cas Shun15:06:43

except to the previous conversation, there is no language spec... and not even unquote is officially documented it seems?

Suni Masuno15:06:15

that's where I've been living

Cas Shun15:06:22

right now I feel frustrated that I have to go read blogs about how a core language feature is supposed to work

Suni Masuno15:06:56

If you're a #3 person, you can go direct to source. I've done it a few times in other languages. https://github.com/clojure/clojure/

Cas Shun15:06:36

@suni_masuno that requires quite a bit of bootstrapping I think? In order to learn how to use the language, I need to know how to use the language?

Suni Masuno15:06:43

I was early on some of the JS frameworks and that was all we had.

lilactown15:06:45

I think thereโ€™s different kinds of documentation. A lot of the details of Clojure are documented perfectly fine as a reference (e.g. you know what youโ€™re looking for and youโ€™re experienced with the language), but not great as an introduction to some of the ideas it uses

lilactown15:06:11

which is frustrating at times

Cas Shun15:06:31

yes, that's how I feel

Cas Shun15:06:44

when I know what I need it's usually not too difficult to find information

Cas Shun15:06:50

when I have no clue, it's very difficult

Cas Shun15:06:14

compared to java, which I've started learning recently, it's very easy to figure out out what I need to know AND what I know I need to know

lilactown15:06:39

yep. I hear you

Cas Shun15:06:45

browsing various things for python and javascript, they also seem to have lots of official documentation on HOW to do things

Cas Shun15:06:37

I almost feel like I know javascript better than clojure just trying to mess with VSCode to use clojure with. (I'm using emacs now)

Cas Shun15:06:45

er, typescript I guess

Suni Masuno15:06:47

It's more of a target demographic problem. it can be very hard to find anything high level in those languages, because the vast majority of the resources are being made for first year devs. While here it's hard to find things for first year devs, but easy to find things for 10th year devs (which are HARD to find over there) But really those are community measurements, not language.

Suni Masuno15:06:41

If I had a dollar for every "this is what a variable is" paragraph that was between me and a real answer in the official blogs in JS... >.>

Cas Shun15:06:17

I'd be ok with that. It's easier to scan over a paragraph than to be unable to find an answer at all.

lilactown15:06:51

more troubling is the number of blog posts / guides in JS that are wrong. but thatโ€™s neither here-nor-there

Suni Masuno15:06:09

Fair. The real thing here is people write for themselves (whether they intend to or not) so that just means that's who's in the community. If you join us, and write, you'll bring the missing voice.

lilactown15:06:22

I recognize the problem youโ€™re talking about @auroraminor. JavaScript, Java, Python have put a ton of resources into their onboarding documentation and it shows

Suni Masuno15:06:38

But we can't become who we once were (despite how much happier I'd be about my waistline if I could -_-)

dpsutton15:06:53

@auroraminor can you point me to a particularly helpful official documentation in python or some other language?

dpsutton15:06:05

(not challenging, but want an example of what would help you)

lilactown15:06:20

itโ€™s also helpful to have someone in your immediate vicinity who is good at the language, which is usually so at school or friends when using JS, Python, Java, not so much for Clojure unless youโ€™re lucky

Cas Shun15:06:56

@dpsutton I've read through the official python docs, and I feel they do a good job of explaining what something is, how to use it, and why

Suni Masuno15:06:03

(side note, really great point that is transparent to a lot of us who can't have your perspective! ^_^)

Cas Shun15:06:22

http://clojuredocs.org helps with what (sometimes) and how... but almost never why

Cas Shun15:06:36

and often the examples are really obtuse

Suni Masuno15:06:53

Yeah, python was written with the official stated intent that you should be able to learn it by "Reading the docs like a novel" which it's seeming more and more every year was a freak'n great idea.

Cas Shun15:06:12

and while I understand that clojure rarely breaks things, relying on a community effort seems like it could be error-prone

Cas Shun15:06:48

I assume it gets docs directly from a specific version, but who knows if the examples are any good, relevant or idiomatic?

Cas Shun15:06:15

I had an example of that yesterday, but I can't remember what it was. A core function I was trying to use and all of the examples were silly complex

Suni Masuno16:06:27

If doc quality is a primary way you learn python and haskel are likely your favs then. They focus greatly on that. While that isn't so much a motivator for me, I do understand it. Or you could write-as-you-learn and fix the problem. ^_^ but that's a lot of time.

Cas Shun16:06:44

reduce is a good example maybe

Cas Shun16:06:31

the docstring was confusing to me about what f should accept and the order, and all of the examples on https://clojuredocs.org/clojure.core/reduce were confusing to me

lilactown16:06:51

haskell is actually terrible. worse than Clojure IME

Cas Shun16:06:52

this was a few months ago, but I remember being really frustrated by it

Cas Shun16:06:00

because I understood what reduce is, but not how to use it

lilactown16:06:28

yes, the docstring for anything that takes a function is really opaque in Clojure IMO

lilactown16:06:02

I know itโ€™s a stylistic thing for Rich, but seriously: give me a literal example in the docstring

lilactown16:06:41

Iโ€™ve just memorized that the arguments for reduce are (fn [accumulator current] ...)

lilactown16:06:57

but itโ€™s hard to know how many arguments my function should take when passing it in, what order they are

lilactown16:06:03

when reading it written in long-form

Cas Shun16:06:13

if somewhere it said 'the first to f argument is...'

Cas Shun16:06:16

I'd have got it in seconds

lilactown16:06:40

anyway this is getting ranty ๐Ÿ˜› but just wanted to let you know you are heard, @auroraminor

โœ… 4
lilactown16:06:59

IMO the payoff of Clojure is pretty great, because once youโ€™re familiar with it, other languages will follow quickly

Suni Masuno16:06:13

Is that the actual code from which the API is generated? And would a PR there actually address (that specific) issue?

lilactown16:06:14

but if youโ€™re looking for greatest leverage soonest, I think Python and JS are also a great choice

Cas Shun16:06:23

that is why I chose clojure

Cas Shun16:06:44

I did a lot of research and saw that lisps and functional languages make it easier to understand common concepts

Cas Shun16:06:52

so I chose a functional lisp that seemed practical

Cas Shun16:06:01

which left basically just clojure and sbcl

zane16:06:32

It's interesting to me that you'd put SBCL over Racket as far as practicality goes.

zane16:06:06

Not arguing. Would just be curious to hear your thinking there.

bronsa16:06:26

uh? that's the common position really

Cas Shun16:06:45

I tried racket for a few days, and when I started researching ahead of myself on how to do something like draw a UI or use common databases... it seemed surprisingly difficult

Cas Shun16:06:50

and clojure seemed very simple

bronsa16:06:23

common lisp has always been a massively pragmatic language, racket is a great language but it's definitely suffered a bit in the past due to its pedagogical/academic background, and lacking many real-world libraries

zane16:06:28

> uh? that's the common position really News to me, and if so I'm not sure I agree. ๐Ÿ™‚

zane16:06:12

Thanks, Cas.

Cas Shun16:06:16

@bronsa that was exactly what I found after a day of non-stop googling. "Ok, that's cool. So how do I draw a window with that information"

Cas Shun16:06:27

and racket seemed difficult at that point

bronsa16:06:41

I mean, racket was originally created as a pedagogical language (back when it was called PLT scheme), not as a practical language, that's not really an opinion

bronsa16:06:53

common lisp on the other hand was created out of industry needs

zane16:06:57

I'm aware of Racket's background.

Cas Shun16:06:10

I think I would have gone with some sort of common lisp, but I couldn't get anything working!

bronsa16:06:39

I think racket is a great language and things have been improving quickly but the common lisp ecosystem is still massively more vast than the racket one

bronsa16:06:47

both in terms of libraries, maturity and community

Cas Shun16:06:48

and it was implied that you were definitely using emacs, which I wasn't, and that was another layer of confusion

Cas Shun16:06:56

ironically, I'm happily using emacs now though

bronsa16:06:12

SBCL is widely used in industry compared to racket

bronsa16:06:29

and has had many many many more man hours been poured into it

zane16:06:55

You're pretty passionate about SBCL.

bronsa16:06:10

not particularly

Cas Shun16:06:46

@U050CT4HR did you learn/use/attempt/mess with racket?

zane16:06:47

Well, yes. As a teaching assistant I used to use Racket to teach undergraduates how to program.

Cas Shun16:06:12

do you feel that it has value over clojure in that capacity?

bronsa16:06:15

it's a great language at that

zane16:06:29

Emphatically yes.

Cas Shun16:06:00

why racket over clojure in that capacity?

zane16:06:03

I'm also of the opinion that Racket has made great strides in the last 10 years or so when it comes to "practicality", but because of its history as a pedagogical language sometimes those advances get overlooked.

zane16:06:02

> why racket over clojure in that capacity? There are a few different reasons, but one of the biggest ones in my view is the notion of teaching languages.

Cas Shun16:06:18

can you elaborate? I'm very interested in this

zane16:06:20

Racket isn't just a programming language, it's a system for creating other programming languages. The people behind Racket used it to create a set of "teaching languages" that start small and introduce concepts incrementally. https://docs.racket-lang.org/drracket/htdp-langs.html

bronsa16:06:39

the docs for racket are also astoundingly good

๐Ÿ™‚ 4
zane16:06:52

Also DrRacket is a great IDE to learn to program in. There are lots of reasons.

bronsa16:06:24

it's fair to say though that if you want to learn a language you can realistically find a job for, racket isn't really what you should be learning

bronsa16:06:20

nor is common lisp i'm sad to say, although some may disagree

zane16:06:29

No doubt there are fewer Racket jobs.

bronsa16:06:54

"fewer" is an understatement for a few orders of magnitude less

zane16:06:04

Absolutely.

zane16:06:13

I think the answer to the question of which language one should start with has a lot to do with how much pressure one feels to find a job quickly.

bronsa16:06:20

absolutely

bronsa16:06:36

if there's no pressure for finding a job, racket is massively valuable and an incredible introduction

zane16:06:07

One can then branch out and learn other languages, and doing so will be easier because of the strong foundation that "How to Design Programs" provides.

zane16:06:17

But that only works if you've got the time.

โž• 4
Cas Shun16:06:32

@U050CT4HR did you follow the official racket tutorials much in your TA work?

Cas Shun16:06:40

or work off a separate curriculum?

zane16:06:00

We taught "How to Design Programs" in particular. https://htdp.org/

zane16:06:32

I will say that despite its origins Racket has made significant strides in the area of practicality over the last decade, and that the Racket community has been struggling to get the messaging out about those advances.

zane16:06:01

In fact, my understanding is that the change of names from "PLT Scheme" to "Racket" was in part an attempt to fix their branding problem.

zane16:06:36

Leaving aside whether or not it's accurate the perception persists, as is evidenced by Nicola's comments. ๐Ÿ˜…

Cas Shun16:06:14

one of those things

Cora (she/her)16:06:27

I have to look up reduce's f arg order every time and every time I'm frustrated by the docs

Cora (she/her)16:06:14

the gotcha is that this sentence "then applying f to that result and the 3rd item" actually includes the argument order which is just not obvious at all

Cas Shun16:06:17

yeah, I understand that now

dpsutton16:06:28

the arity without the initial value needs to go away. then it gets much clearer

Cas Shun16:06:38

but the idea that 'applying' implies the order is not obvious to me at all (and I'm a technical writer by trade)

dpsutton16:06:48

2 arg reduce should be a lint error ๐Ÿ™‚

Cora (she/her)16:06:53

I mean like last night I looked again and about lost my mind at the terrible examples

Cas Shun16:06:40

there's quite a few examples like that where the docstring makes no sense to me, and the examples on clojuredocs make it even more confusing

Cora (she/her)16:06:46

@auroraminor totally agree, which is what I meant to point out, that you're not alone in being frustrated by the docs

Cas Shun16:06:01

I feel like unquote is a great example. no docstring AND no examples

Cas Shun16:06:11

and yet it's clearly an integral and important part of the language

Suni Masuno16:06:21

I feel like no one is arguing against you so... where ya going with this brah?

Cas Shun16:06:44

@suni_masuno not all conversations have to be arguments

andy.fingerhut16:06:55

If you want examples, http://ClojureDocs.org is in many cases better than the built-in doc strings. And you can easily add more with a free account if you wish.

Suni Masuno16:06:07

Yeah, agreed. Still not adverse so... what does it add up to?

Cas Shun16:06:38

@suni_masuno I don't know, but I've already learned some things during this conversation by just chatting. So it's had value to me

Cora (she/her)16:06:03

I might go and try to add a better reduce example

Cas Shun16:06:16

@andy.fingerhut thank you for your plethora of examples there, however it's difficult to feel confident enough to add an example when I don't feel like I understand something.

andy.fingerhut16:06:54

Understood. You could propose something here and get feedback from others to improve it.

Cora (she/her)16:06:36

so that's another good thing that's come out of it

Suni Masuno16:06:21

Is there perhaps a pattern in what is sub-optimal in the docs? Something that could be improved?

Cas Shun16:06:50

@suni_masuno I've been browsing for that during this conversation

Cora (she/her)16:06:00

using function shorthand in (all) examples is pretty unhelpful since variable names are useful

Cas Shun16:06:05

I think one thing is that almost anything where an argument is a function, seems to be opaque

Cora (she/her)16:06:09

they communicate

andy.fingerhut16:06:21

If you find that you want to replace all doc strings, not just a few, then you will likely find that creating your own, perhaps through collaboration on a site like http://ClojureDocs.org, or some other means, is going to be less frustrating than attempting to get the official doc strings to change.

4
Cas Shun16:06:36

also, clojure apparently has idioms for argument names, but they aren't explicitly discussed anywhere I can see?

Cas Shun16:06:40

f n x xs etc...

Cas Shun16:06:54

I know what these are after many months now, but when I was trying to learn it was frustrating

Cora (she/her)16:06:17

in clojure.spec the args really need more explanation, particularly what are valid for preds

Suni Masuno16:06:08

Interesting, so if perhaps the clojuredocs page hyperlinked the args to descriptions of their shorthand it would short circuit a problem?

Cas Shun16:06:11

clojure.spec is a good one. I understand the talks I think, but when I tried to read the docs, I feel like I really have no clue.

Cora (she/her)16:06:18

that's a great point about the arg name conventions

Cora (she/her)16:06:39

@suni_masuno oooh that's a great idea

Suni Masuno16:06:51

I'm not familiar with the way clojuredocs is implemented, but I've seen similar features in project docs in other languages using a regex match for common key phrases getting links.

andy.fingerhut16:06:11

Yeah, arg name conventions would be nice to have described somewhere "central", but not sure yet where a good place would be...

andy.fingerhut16:06:06

Perhaps a new 'guide' article to add to the existing ones here? https://clojure.org/guides/getting_started

Cas Shun16:06:36

a glossary of some sort would be nice

Cas Shun16:06:57

like the 'reading clojure characters' article, that's helped me SO much because it's basically a glossary of common terms

andy.fingerhut16:06:04

For 'spec' docs in particular, enhancing the existing guide is probably the best way to go: https://clojure.org/guides/spec

Cas Shun16:06:36

I wouldn't even know where to start, except that the spec guide basically de-learns what I feel like I learned by watching talks about spec ๐Ÿ˜ž

Cora (she/her)16:06:27

@auroraminor fwiw I found Getting Clojure to be a super gentle introduction

Cora (she/her)16:06:45

not sure if it's first-language gentle, but I've never read such a gentle intro to a language

๐Ÿ’ฏ 4
Cora (she/her)16:06:41

centralizing a doc is good in the sense that you don't duplicate, but then it's often not where you need it, at the point where you have a question

Cas Shun16:06:50

Excellent, thank you. I still feel like I have massive holes in my fundamental understanding of the language. This looks great

Cas Shun16:06:18

I made the 'mistake' of reading "Programming Clojure" first, and multiple times since. I still don't understand probably 75% of that book.

Cora (she/her)16:06:18

their intro to java was the simplest I've ever seen, I really loved it

Cora (she/her)16:06:15

it has "in the wild" sections where you'll see the features being used in the community

Cora (she/her)16:06:31

"staying out of trouble" has warnings and gotchas to look out for

Cora (she/her)16:06:38

@auroraminor I know it's not clojure but this section on elixir unquoting is what did the trick for me: https://elixir-lang.org/getting-started/meta/quote-and-unquote.html#unquoting

Cora (she/her)16:06:55

iex> number = 13
iex> Macro.to_string(quote do: 11 + number)
"11 + number"

Cora (she/her)16:06:03

clearly they wanted it to be 11 + 13

Cora (she/her)16:06:10

iex> number = 13
iex> Macro.to_string(quote do: 11 + unquote(number))
"11 + 13"

Cora (she/her)16:06:24

so unquote evaluates it in the context where the macro is being defined

Cora (she/her)16:06:58

granted I've only written a toy macro in clojure

Cas Shun16:06:03

@corasaurus-hex the thing that's tripping me up, I think, is figuring out what happens at macroexpansion, and what gets eval'd. Like why can't I just use x from the defmacro, is it not 'in scope'?

Suni Masuno16:06:29

macroexpand-1 was the trick for me, playing with ideas in a repl

Cas Shun16:06:02

I used macroexpand-1 for a few hours before asking here ๐Ÿ˜ž

Cas Shun16:06:13

I knew what was wrong, but not how to fix it

Cas Shun16:06:36

now I know how to fix it, but not totally clear on why it works

Cora (she/her)16:06:28

are you unclear on how it ends up substituted in or are you unclear on how the ~ itself does its thing?

Cora (she/her)16:06:13

I think this is a pretty good, simple example

Cora (she/her)16:06:15

user=> (defmacro add-1 [num] `(+ 1 ~num))
#'user/add-1
user=> (macroexpand-1 '(add-1 (* 2 3)))
(clojure.core/+ 1 (* 2 3))

Cora (she/her)16:06:45

the macro won't even run the arguments you pass to it, it'll just stick those into the template you provide, basically

Cora (she/her)16:06:17

once the template is setup it'll eval the code, is my understanding

Cora (she/her)16:06:05

user=> (macroexpand-1 '(add-1 7))
(clojure.core/+ 1 7)

Cas Shun16:06:27

trying some things at repl now

Cas Shun16:06:15

(defmacro atest [x]
  `(let [g# '~x]
     g#))

(defmacro btest [x]
  `('~x))

(macroexpand-1 (atest y))
;; => y
(macroexpand-1 (btest y))
;; => Wrong number of args (0) passed to: clojure.lang.Symbol

Cora (she/her)16:06:17

so this is why they put the # at the end of variables in those macro quotes:

user=> (def my-num 123)
#'user/my-num
user=> (defmacro add-1 [my-num] `(+ 1 my-num))
#'user/add-1
user=> (macroexpand-1 '(add-1 7))
(clojure.core/+ 1 user/my-num)
user=> (add-1 7)
124

Cas Shun16:06:43

so I'm unclear on why it works with let and gensym, but not just by itself?

Cas Shun16:06:11

I would think that btest would give (y)?

Cas Shun16:06:25

or (quote y)?

Cora (she/her)16:06:32

can you drop the parens ('~x)

Cora (she/her)16:06:58

it's trying to run it as a function, I think

Cas Shun16:06:59

ok, that works

Cora (she/her)16:06:06

which takes an argument

andy.fingerhut16:06:07

Try (macroexpand-1 '(btest y))

Cas Shun16:06:20

but doesn't ` quote what's inside?

Cas Shun16:06:29

I don't understand why it's trying to run it as a function

Cora (she/her)16:06:33

oh, right, macroexpand expects the thing to be quoted

Cas Shun16:06:55

ok now I'm even MORE confused...

andy.fingerhut16:06:57

It expects an expression to be macroexpanded. If you do not quote it, it will be eval'd, and the result will be passed to macroexpand-1

andy.fingerhut17:06:46

macroexpand-1 is a function, so all of its arguments are eval'd before the function is called.

Cas Shun17:06:58

so if I quote what I send to macroexpand-1, btest does give (quote y) like I expected, but atest is (clojure.core/let [g__21536__auto__ 'y] g__21536__auto__)

andy.fingerhut17:06:33

When I do it, (macroexpand-1 '(btest y)) gives ((quote y)), which is not the same as (quote y)

Cas Shun17:06:07

@andy.fingerhut that's correct, poor paste by me.

andy.fingerhut17:06:14

Parentheses are not optional things you can just add on or remove in Clojure, without changing the meaning of an expression, the way they can be in some other programming languages.

Cora (she/her)17:06:55

sometimes it's a little confusing at the start where they go and what they're doing in that context

Cora (she/her)17:06:08

like there are parens that definitely aren't invoking a function

Cora (she/her)17:06:24

like with multi-arity functions where each arity is wrapped in parens

Cora (she/her)17:06:47

so it can be a little confusing to understand them all at first

Cora (she/her)17:06:13

it's nice that defn and whatnot are so slick but it definitely makes the syntax feel a lot less consistent to newcomers

Cora (she/her)17:06:30

knowing that they're macros really helps

Cora (she/her)17:06:38

but you need to know what macros are first ๐Ÿ™‚

andy.fingerhut17:06:47

So (macroexpand-1 '(atest y)) does expand to (clojure.core/let [g__2__auto__ (quote y)] g__2__auto__) when I eval that in a REPL. The only reason you see different numbers inside the g__2__auto__ symbol name is because you have been running that REPL much longer than I have mine. The just get auto-numbered names.

Cas Shun17:06:16

yes, I understand the gensym thing (hopefully)

andy.fingerhut17:06:39

(clojure.core/let [g__2__auto__ (quote y)] g__2__auto__) means exactly the same thing as (clojure.core/let [x (quote y)] x). It just has a different name than x that has been auto-generated while back-quoted expression was being read.

Cas Shun17:06:09

so

(defmacro btest [x]
  `~x)

(defmacro ctest [x]
  'x)

(macroexpand-1 '(btest y))
;; => y
(macroexpand-1 '(ctest y))
;; => x

Cas Shun17:06:30

I believe I understand ctest. I'm quoting x, I get x.

Cas Shun17:06:03

btest... is `~ what allows me to get x from the defmacro bindings?

Cas Shun17:06:17

is there another way to get that x's value that's passed to the macro?

Cora (she/her)17:06:52

it's the only way I know how

andy.fingerhut17:06:25

user=> (defmacro dtest [x] x)
#'user/dtest
user=> (macroexpand-1 '(dtest y))
y

Cora (she/her)17:06:38

macros are executed, so there's this too:

user=> (defmacro add-1 [n] (println "hello: " n) `(+ 1 ~n))
#'user/add-1
user=> (add-1 7)
hello:  7
8
user=> (add-1 (* 2 3))
hello:  (* 2 3)
7

andy.fingerhut17:06:46

One way to think of backquoted expressions is like a little "template". You don't have to use backquoted expressions ever when defining a macro, or at any other time. You can instead write Clojure code inside of a macro definition that "builds" and returns the expression you want.

Schmoho17:06:03

I had this issue with CIDER lately - when I use lein on jack-in I don't start in the main namespace of the lein project and the user-ns has no clojure.repl-ns preloaded. I was told this should not be so, but I wanted to be sure, so could someone tell me whether this is indeed unexpected behaviour?

dpsutton17:06:42

come to #cider

๐Ÿ‘ 4
andy.fingerhut17:06:05

Every macro that can be written with backquote can be written without backquote.

Cora (she/her)17:06:17

I don't think they have used templates before?

andy.fingerhut17:06:44

The backquoted expressions are a convenience that are useful for many kinds of macros, which can make it easier to write those macros.

Cas Shun17:06:52

@andy.fingerhut my original example was

(defmacro testmacro [x]
 `(let [form# '~x
        val# ~x]
    {:val val# :form form#}))

Cas Shun17:06:02

where I was confused why the '~ was necessary

andy.fingerhut17:06:45

It isn't "necessary". You could write that entire macro without using backquote at all -- it would just be a bit harder to write and read.

andy.fingerhut17:06:18

I mean, I'm being a bit pedantic there. But there is nothing about Clojure macros that requires you to use backquote at all.

Cas Shun17:06:32

pedantic is nice, it's what I need

andy.fingerhut17:06:33

If you do use a backquote expression, then the rules are that every symbol inside of that backquoted expression is resolved into its fully qualified version, with a namespace. Every symbol that doesn't have ' ` ~ stuff in front of it that is.

Cora (she/her)17:06:46

user=> (defmacro add-1 [n] (list '+ 1 n))
#'user/add-1
user=> (add-1 5)
6

andy.fingerhut17:06:06

So if you have this template, and you don't want the symbol to just become fully namespace qualified and keep its name, you need a syntax to inform the Clojure reader to do something different with it.

andy.fingerhut17:06:21

So, let me look at your code example then, where I guess the question is why does it seem like 'x is needed in one place, but x is good enough in a different place?

Cas Shun17:06:53

@andy.fingerhut the contrary is without the syntax quote

(defmacro testmacro [x]
  (let [form# x
        val# ???]
    {:val val# :form form#}))
I was here originally and didn't understand how to evaluate x to get the eval'd value of it.

andy.fingerhut17:06:31

You want a macro such that you can write (testmacro some-expression) in your code, and it is transformed, before the compiler gets a crack at it, into {:val some-expression :form (quote some-expression)} ?

Cas Shun17:06:42

yes, that is correct. I was just writing that.

Cas Shun17:06:48

sorry for my unclearness of what I want to happen

andy.fingerhut17:06:19

Here is one way that doesn't use backquote at all:

andy.fingerhut17:06:21

user=> (defmacro tmacro [expr]
{:val expr :form (list 'quote expr)})
#'user/tmacro
user=> (macroexpand-1 '(tmacro (+ 5 7)))
{:val (+ 5 7), :form (quote (+ 5 7))}
user=> (tmacro (+ 5 7))
{:val 12, :form (+ 5 7)}

andy.fingerhut17:06:05

I suspect most people would use a backquoted expression more commonly for this. I just wanted to demonstrate that it wasn't needed.

andy.fingerhut17:06:42

This variation also does not use backquote at all:

andy.fingerhut17:06:45

user=> (defmacro tmacro2 [expr]
(let [form (list 'quote expr)
      val expr]
  {:val val :form form}))
#'user/tmacro2
user=> (macroexpand-1 '(tmacro2 (+ 5 7)))
{:val (+ 5 7), :form (quote (+ 5 7))}
user=> (tmacro2 (+ 5 7))
{:val 12, :form (+ 5 7)}

Cas Shun17:06:59

hmm, let me try

andy.fingerhut17:06:26

This is about the simplest version I can think of that does use backquote:

Cas Shun17:06:27

that (list 'quot part is throwing me a bit

andy.fingerhut17:06:28

user=> (defmacro tmacro3 [expr]
`{:val ~expr :form '~expr})
#'user/tmacro3
user=> (macroexpand-1 '(tmacro3 (+ 5 7)))
{:val (+ 5 7), :form (quote (+ 5 7))}
user=> (tmacro3 (+ 5 7))
{:val 12, :form (+ 5 7)}

andy.fingerhut17:06:32

If you have a macro body that has no backquoted expressions in it, it is a Clojure function that takes an unevaluated expression as input, and returns an unevaluated expression as output, that will then be evaluated by the compiler as if you had typed it that way.

andy.fingerhut17:06:12

user=> (def expr '(+ 5 7))
#'user/expr
user=> (list 'quote expr)
(quote (+ 5 7))

andy.fingerhut17:06:40

Not sure if I am making your brain hurt more, or less, right now ๐Ÿ™‚

Cas Shun17:06:48

you're making it work at least

Cas Shun17:06:32

@andy.fingerhut so in your example, how would I get the evaluation of the expr without using backquote?

Cas Shun17:06:02

I am curious if there is a solution besides (eval)

andy.fingerhut17:06:21

Does tmacro3 not do what you ask?

andy.fingerhut17:06:43

Sorry I meant tmacro1

andy.fingerhut17:06:16

Grr. This one:

user=> (defmacro tmacro [expr]
{:val expr :form (list 'quote expr)})
#'user/tmacro
user=> (macroexpand-1 '(tmacro (+ 5 7)))
{:val (+ 5 7), :form (quote (+ 5 7))}
user=> (tmacro (+ 5 7))
{:val 12, :form (+ 5 7)}

Cas Shun17:06:46

yes... hold on

andy.fingerhut17:06:49

the evaluated (+ 5 7), or 12, is in the return value of the last expression

Cas Shun17:06:26

I was looking at the output of macroexpand, not using the macro ๐Ÿ™‚

Cas Shun17:06:08

ok, I think I understand this now... until I try to write another macro I'm sure ๐Ÿ™‚

andy.fingerhut17:06:58

backquoted expressions still confuse me, when I see combinations of ' ~ etc in front of symbols inside of those backquoted expressions. I don't use them that often, because I don't use macros that often.

Cas Shun17:06:43

the backquote (syntax quote?) and unquote seem to be poorly explained in various sources

Cas Shun17:06:51

and from what I can see, there's no official docs at all

andy.fingerhut17:06:30

Your example seems to be a place where a macro is actually necessary, since you want to preserve an expression unevaluated in the result, so you are not reaching for one frivolously here.

Cas Shun17:06:42

and when most of the macros that I tried to read the source of use various combinations of holding shift and face-rolling the number row...

Cas Shun17:06:46

it's getting super confusing

bronsa17:06:24

there's some great material on syntax quote in the common lisp book "on lisp", which applies to clojure just by replacing , with ~ (the first is the common lisp version of unquote) if you're looking for some good explanations

bronsa17:06:55

one more way to learn how this works is to realize that syntax-quote is merely sugar over list manipulating functions and quote, and try to figure out what each expression would correspond to

Cas Shun17:06:09

I feel like I'm past wanting good explanations, and I just want good documentation ๐Ÿ˜ž I will pick up the book though, thank you.

bronsa17:06:46

for example:

`(foo [email protected](bar baz))
is equivalent to
(concat (list (quote foo))) (list bar baz))

bronsa17:06:11

as a first step I'd suggest you try implementing your macros w/o using syntax-quote at all

Cas Shun17:06:29

well, that's what I would have done if there were any examples of people doing that

bronsa17:06:31

just as normal list manipulations

bronsa17:06:19

I'd also like to remind you my previous suggestion to ignore macros alltogether until you're comfortable writing normal clojure :) macros really aren't used that much at all

bronsa17:06:40

they're advanced features that most users of clojure will only ever need to implement once or twice in years

Cas Shun17:06:49

I did think that my use for a macro here was ok, since I wanted the actual expression and its evaluated value

Cas Shun17:06:55

is that incorrect?

bronsa17:06:04

no it's perfectly fine

Cas Shun17:06:17

I did google around for misuse of macros because I heard that most things can be done with functions

bronsa17:06:28

but are you trying to do this because of actual need or just as an exercise?

Cas Shun17:06:26

I think it's an actual need. I was writing a macro to wrap cognitect.rebl/submit, which wants the expression and value. I also wanted to add some extra output for myself

Cas Shun17:06:57

I have no clue why I'm using REBL to begin with... it just seemed neat

bronsa17:06:16

ok, then what I like to remind people approaching macros for the first time is that: 1- syntax quote is not necessary to write macros 2- there's nothing "special" about syntax-quote and its usage in macros, it's just sugar for creating lisp values that can be done exactly the same using normal list manipulating functions 3- macros are literally normal functions that take as input a list expression and return a list expression, they're simply executed at an earlier stage than normal functions

๐Ÿ‘ 4
bronsa17:06:35

with that in mind, you should first try to implement your macro as a normal function over a quoted list

bronsa17:06:40

not using syntax-quote

bronsa17:06:17

if you're not able to do that yet, that's a sign that you're maybe trying to run before you can walk, and I'd recommend focussing on getting comfortable with manipulating lists and normal values, since that's really what you're trying to do

bronsa17:06:04

does that make sense?

Cas Shun17:06:33

yes, absolutely

Cas Shun17:06:58

I would have much preferred to start that way in fact, but I was working based off the articles/examples I found

Cas Shun17:06:22

which all made it seem like there was a macro-specific sub-language to be used

bronsa17:06:01

there's no such thing, syntax-quote can be used in normal functions, it just so happens that it's mainly used to implement macros

Cas Shun17:06:20

yes, I figured that out somewhere in the middle of this ๐Ÿ™‚

bronsa17:06:48

(my phrase may be considered a bit disingenuous, it's undoubtly true that syntax-quote would not exist if it wasn't for helping writing macros, but that doesn't mean it's its purpose or that it has any special realationship with macros)

Cas Shun17:06:34

it does seem strange to me that we work with sequences constantly using these functions, but when writing a macro you're idiomatically expected to use a different set of tools which correspond to the normal tooling anyway

bronsa17:06:48

well, it's just more concise

Cas Shun18:06:10

we're not writing things with pencils though ๐Ÿ˜‰

bronsa18:06:43

writing complex macros w/o using syntax-quote would result in code orders of magnitude harder to parse for a human

bronsa18:06:53

even if the resulting code would be absolutely identical to the compiler

Cas Shun18:06:04

I would need to encounter those to fully understand I think

Cas Shun18:06:20

or just find some core macros and try to de-quotify them

lilactown18:06:28

yep. Iโ€™m writing a macro right this second, that is very readable using syntax-quote:

(defmacro defnc
  "Defines a new React component."
  [display-name props-bindings & body]
  (let [usables (find-all hook?
                          body)]
    `(do
       (defn ~display-name [props#]
         (let [~props-bindings [(->props props#)]] ;; `props` is a proxy to `bean`
           [email protected]))
       (signature ~display-name (list [email protected](map str usables)))
       (register ~display-name ~(str display-name)))))

bronsa18:06:48

a fun trick: sticking a ' in front of a syntax-quoted expression will show you what it desugars to

bronsa18:06:21

take

`(foo [email protected][1 2] '~3)
for example

bronsa18:06:32

user=> '`(foo [email protected][1 2] '~3)
(clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/foo)) [1 2] (clojure.core/list (clojure.core/seq (clojure.core/concat (clojure.core/list (quote quote)) (clojure.core/list 3))))))

bronsa18:06:51

the same effect can be obtained by using read-string on the string representation

bronsa18:06:05

user=> (read-string "`(foo [email protected][1 2] '~3)")
(clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/foo)) [1 2] (clojure.core/list (clojure.core/seq (clojure.core/concat (clojure.core/list (quote quote)) (clojure.core/list 3))))))

bronsa18:06:38

understanding why this works may be a bit too complex for now, but nonetheless it could be a good way for you to see what a syntax quoted expression expands to and get an understanding of how syntax-quote and unquote work

Cas Shun18:06:54

excellent, that is very helpful thank you

drewverlee21:06:44

will a timeout expring on a core async thread cause the work being done on that thread to stop?

hiredman21:06:52

a timeout is a channel

hiredman21:06:12

your question doesn't really make sense

hiredman21:06:58

a timeout is a channel that closes after a certain amount of time has passed, it has nothing what so ever to do with threads

drewverlee21:06:39

right, yea. I got my wires crossed for a second. The timeout applies to the channel.

hiredman21:06:49

a timeout is a channel

hiredman21:06:57

it is not something that applies to a channel

drewverlee21:06:18

i'm now confused about you mean

hiredman21:06:27

the docstring says it

hiredman21:06:30

a timeout is a channel

drewverlee21:06:39

it doesnt say that though

hiredman21:06:47

Returns a channel that will close after msecs

hiredman21:06:49

you can imagine timeout as being something like (defn timeout [ms] (let [c (chan)] (future (Thread/sleep ms) (async/close! c)) c))

hiredman21:06:12

it is a channel, not an operation on channels, or something you can apply to a channel

hiredman21:06:16

sorry, left out the returning the channel

hiredman21:06:05

it isn't implemented like that because it tries to optimize the timeouts and limit the number of channels created, but that is the naive implementation

hiredman21:06:42

so if you have something like (alt! (timeout 100) ([...] ...) C ([...] ...)) that isn't a timeout on the channel C, that is two channel operations (a read from the timeout channel or a read from C) being chosen between

hiredman21:06:53

so replace the timeout channel with X, and ask your self, if alts chooses to read from X, why would that do anything to C

hiredman21:06:20

and then the further question is, given C is a channel, why would anything happening to C stop a thread from executing

hiredman21:06:00

channels are not futures

drewverlee21:06:52

hmm ok. After reading this and reviewing core async, i'm probably looking at the wrong tool. I want to spawn a couple separate threads and have them do work but have a way for them to short circuit after some amount of time. Futures will place the body on another thread. I suppose stopping the work is something that has to be custom built? Like i can build a while loop that stops after a number of iterations or after some amount of time passes. There is nothing that generically wraps some body of code evals it tell some amount of time.

noisesmith21:06:22

future-cancel works if the future in question spends time waiting on io or sleeping

noisesmith21:06:29

you would do that from another thread

noisesmith21:06:47

there are task scheduling tools like executors that come with the vm as well

noisesmith22:06:35

most poetic doc string in clojure.core

(ins)user=> (doc future-cancel)
-------------------------
clojure.core/future-cancel
([f])
  Cancels the future, if possible.
nil

andy.fingerhut22:06:56

And there are warnings about using the underlying Java cancel method, and/or attempting to forcefully stop a thread, etc, that are easy to find via Google searches. Having some thread/task/whatever periodically check whether some "master" wants it to stop doing its work, enables you to write code so that thread/etc. stops cleanly, with the tradeoff that infinite or long-running loops in your code between such checks will increase how long of a time can elapse before it stops doing work.

noisesmith22:06:22

yeah - but in my experience it's very common for long running tasks that you might want to cancel to either sleep or wait on IO

noisesmith22:06:30

and those are both things that make a task cancellable

noisesmith22:06:59

another pattern is to use a promise or delay as a one time cancellation switch, exiting a loop if it is realized

johnj22:06:35

Is there a need for shutdown-agents if you already have a System/exit in -main?

noisesmith22:06:16

yes, the agent pool can still delay exit or maybe not

Cora (she/her)22:06:17

hmmm component reminds me of some of the facilities in elixir/erlang

johnj22:06:04

@noisesmith I'm not using any agents but I guess some lib might? or is this uncommon

noisesmith22:06:28

it's more common to use the agent thread pool via future actually

noisesmith22:06:48

future uses the send-off pool

johnj22:06:49

Oh ok, ithe thread pool for agents is "shared"

noisesmith22:06:19

yeah - I don't know if anything but future uses that pool, but future is relatively commonly used

johnj22:06:47

in library code?

noisesmith23:06:21

no, not in library code

โœ… 4
andy.fingerhut23:06:52

future is called by clojure.java.shell/sh and pmap inside of Clojure, so they have similar effect of causing a 60-second delay before a process actually exits if you do not call shutdown-agents or System/exit: https://clojuredocs.org/clojure.core/future

๐Ÿ‘ 8