Fork me on GitHub
#beginners
<
2020-04-14
>
sogaiu00:04:47

@grierson looking at the commit log, clojurevscode dev seems to have resumed -- clover is still in development (early) iiuc

sogaiu00:04:02

at this point i think calva is the most developed

MatD00:04:00

Hi. From what I read, returning nil is the way to go in clojure (instead of Optional or Maybe in a typed language). Is using the some-> macro a good way to handle one function returning nil in a sequence of functions? the documentation of the some-> macro talks a lot about interacting with Java null, so, I just want to be sure I'm using the right abstraction. Thanks

seancorfield00:04:10

@mathieu.durand Yes, if you want to short-circuit a nested sequence of function invocations, some-> is often a way to make it much clearer.

seancorfield00:04:23

It's also fairly idiomatic to have functions accept nil and "do the right thing" if that is reasonable. Not all functions can reasonably accept nil but a lot of core functions are designed to do that -- a.k.a. "nil-punning".

MatD01:04:16

oh good to know ... good idea! thanks @seancorfield

seancorfield01:04:24

(many functions treat nil as an empty sequence where that makes sense)

seancorfield01:04:37

(map inc nil) ;=> () for example

👍 4
seancorfield01:04:17

Also, nil is treated as false in Boolean expressions in Clojure which can also help.

seancorfield01:04:41

(`nil` and false are "falsey", everything else is "truthy")

MatD01:04:19

thanks a lot for the quick answer!

Cris B01:04:33

On that point, is there an equivalent of false? but testing for falsey rather than the value false? I would find it useful in the REPL.

Cris B01:04:03

(or is that 'falseyness'?)

seancorfield01:04:04

@cb.lists do you mean in an if or something?

seancorfield01:04:24

(if x :it's-truthy :it's-falsey)

Cris B01:04:18

I mean when I'm unsure in the REPL whether an expression will return truthy/falsy - a single function to check, rather than use an if

seancorfield01:04:38

I'm not sure what you're asking...

seancorfield01:04:19

If you evaluate something in the REPL and it's nil or it's false -- you'll see that in the REPL and those two values are "falsey".

Cris B01:04:23

Yes of course. I'm just being dense.

seancorfield01:04:53

If you want true from an expression when it evaluates to "falsey", you can just do (not x)

seancorfield01:04:32

(and for completeness, if you need to test that something is any value except nil, you can use (some? x))

Cris B01:04:02

Cheers - the question was a holdover from an early REPL session when using false? puzzled me because I assumed it was a falsy test and hadn't read the doc.

Jim Newton07:04:04

good morning everyone. Can someone explain to me what "lein test" does? Currently I have a single test file test/core_test/core_test.clj which is exactly the one that lein set up for me when it created my project template. In the mean time my source files have been expanded to multiple files and multiple name spaces. Ideally I'd like one testing file per source file, where each testing file tests the functions in the corresponding src file.

Jim Newton07:04:28

how does lein test figure out which file to load and which tests to run?

didibus08:04:03

I think it loads/run all tests defined in your tests folder

Jim Newton07:04:53

I'd like it to run all the tests I've defined even if those tests are defined in different files.

hindol07:04:11

I would assume lein test discovers tests based on the source paths you have configured. As long as you put your tests under test/, it should be picked up.

Aron08:04:49

($ AssetSelectControl {:asset-type (:asset-type @state)})) Why do I need to do this unwrap/wrap? shouldn't this work ($ AssetSelectControl @state) too? (state is just a map with :asset-type set to something non-nil (a string in this case)

andy.fingerhut08:04:02

I have not used react, so am ignorant of the details of how $ works there. Does something go wrong if you use ($ AssetSelectControl @state) ? If so, I would recommend asking in some react-specific channel, or if there isn't one, perhaps in the #clojurescript channel, where more people are probably familiar with react.

Aron08:04:10

$ is helix macro for creating a react component

Aron08:04:52

sorry, element

didibus08:04:39

That should work, unless the helix macro does something weird and expects a literal map passed in

hindol08:04:41

Or, use select-keys.

Aron08:04:15

I am not sure how that helps me learn the situation

Jim Newton08:04:28

I found this blog entry about automatic test coverage using lein, and incorporating it into a ci/cd pipeline https://blog.jeaye.com/2016/12/29/clojure-test-coverage/

Jim Newton08:04:07

Has anyone tried this? It doesn't seem to work for me. I can run it interactively, but none of the ci/cd integration seems to work as predicted.

hindol08:04:31

@ashnur To learn the situation, just try it. If for some reason, you cannot pass extra things in the map, then you can use select-keys as an alternative.

Aron08:04:05

@UJRDALZA5 just trying requires more time that I have. Are you saying that if I can't discover answers on my own because I don't have the time to spend on this discovery process, I don't deserve to understand clojure?

andy.fingerhut08:04:15

I think you will find a lot of developers spend lots of time experimenting when learning new things. Has this not been your experience?

andy.fingerhut08:04:55

You are certainly welcome to ask, and people here will answer if they know, but not everyone in the #beginners channel will be familiar with all libraries.

Aron08:04:00

My experience is that clojure people simply cannot fathom why people who don't yet understand clojure don't want to spend 24/7 experimenting with clojure. I mean, what else can they be doing? I've been trying to use clojure for 8 years now and I was repeatedly blocked by this. Couple of years ago I was told not just to discover the answer on my own, but to write docs! A beginner writing docs to the code that experts don't understand!

hindol08:04:33

@ashnur I did not mean it that way. What I meant is, Clojure REPL makes it insanely easy to try out stuff. Once you connect your editor to the REPL, every evaluation is a keypress away.

andy.fingerhut08:04:59

If you want help with a helix-specific thing, realize that perhaps a small fraction of participants in the #beginners channel will have used it.

andy.fingerhut08:04:24

In the absence of knowledge, the best we can do is suggest experiments to try, or find experts on the helix library.

Aron08:04:29

With respect, I will leave this conversation before I say something that gets me banned.

andy.fingerhut08:04:16

or become knowledgeable about the helix library ourselves, and then answer you, which might take us more time than it would take you to figure out.

didibus08:04:16

Macros do not follow the same rules functions do. A function will evaluate the arguments first, from left to right, and then pass the result of each to the function as the corresponding parameters

👍 4
didibus08:04:52

So they are predictable, and if you want to compute things in the argument before it passes it to the function you can

Jim Newton08:04:06

One problem seems to be that I have to add a dependency in my .lein/profiles.clj file. After that, it works interactivley, i.e., when I run lein from the unix command line. However, the process that runs lein cloverage in the pipeline is run in a docker image which does not have such a ~/.lein directory

andy.fingerhut08:04:12

I am pretty sure that anything you can put in your $HOME/.lein/profiles.clj file can also be put into a project-specific project.clj file.

Jim Newton08:04:42

yes indeed, I put it both places. However, putting it in the project files does not seem to help the lein command line.

hindol08:04:45

~/.lein is for your own convenience. CI/CD will expect the same configuration some other way as it won't have that configured already, unless documented otherwise.

Jim Newton08:04:42

I see in the ci/cd log that indeed the lein-cloverage jar file is being retrieved. However, the lein command line says cloverage is not a task.

Jim Newton08:04:22

so it has read the dependency in the project.clj file. but there's still a problem.

Jim Newton08:04:00

I sent an email to Jaeye, not sure whether he still maintains the tool....

andy.fingerhut08:04:00

Have you tried putting :plugins [[lein-cloverage "1.1.2"]] inside your defproject in your project.clj file?

andy.fingerhut08:04:39

And have no mention of cloverage in your ~/.lein/profiles.clj file?

Jim Newton08:04:18

No, i'll try adding the :plugins. should I remove it from :dependencies ?

hindol08:04:00

Adding as plugin activates the task as well as pull the dependency I think.

Jim Newton08:04:30

Cool. I think it works now. thanks.

andy.fingerhut08:04:02

As a rough rule of thumb, I think :dependencies are for your project, either the normal run time of your project's code, or for test running time (although Leiningen lets you put test-specific dependencies in a different place in the project.clj file, so they are used only during test-running time, not for other 'production' uses of your code).

andy.fingerhut08:04:17

:plugins are for modifying how Leiningen itself works.

Jim Newton08:04:26

Great, the ci pipeline seems to have finished successfully! thanks for the help.

Jim Newton08:04:54

However, there is still something of a mystery.

andy.fingerhut08:04:56

No problem. Glad it is working.

andy.fingerhut08:04:06

What mystery is that?

Jim Newton08:04:14

the docs for cloverage say to add the following to my http://README.md file

Jim Newton08:04:32

[![codecov](]
 ()

Jim Newton08:04:39

But what are USERNAME and PROJECT-NAME ? it doesn't explain. is it my user name on the machine that's running the pipeline? Or is it my name on the local machine where I'm pushing from?

andy.fingerhut08:04:47

I would guess that is completely optional.

Jim Newton08:04:28

optional? I don't think so, because the place where the data is created does not ask for either piece of information.

andy.fingerhut08:04:37

I suspect the intent is that if you wanted some kind of 'badge' in a project that you published on Github, that gave summary information of the output of cloverage run on your project, and it was auto-updated every time you made a commit to the project, you could put that in your project's README

andy.fingerhut08:04:56

If you do not want that effect, you should be able to use cloverage without anything in your http://README.md file

andy.fingerhut08:04:36

Are you saying you want such an auto-updated thing in your project's http://README.md file?

andy.fingerhut08:04:58

I have not set up such an auto-updated 'badge' using Cloverage in any project of mine, nor other similar auto-updated badges. I see that the http://README.md file of the cloverage project itself has one, which you can see the source code of if you clone a copy of the cloverage project. It isn't obvious to me which user name should be used -- my guess would be either your Github username, or a username you must create on the http://codecov.io web site. Others who have done this would be better to confirm those guesses, though.

hindol09:04:00

@U010VP3UY9X That is your Github username and repo name.

Jim Newton09:04:10

thanks for looking. yes it is a mystery. curious thing is that anyone with sufficient permission can push to my get repo, not only ME. and anyone else can read the http://README.md file So somehow the USERNAME has to be independent of person pushing.

Jim Newton09:04:20

what if my project is not on github?

Jim Newton09:04:17

and why would it need MY user name if joe-blow pushes to my git repo?

hindol09:04:23

So, the /gh/ part of the URL says it is Github. For Gitlab it might be /gl/.

Jim Newton09:04:11

I'll add some question/documentation issues there and see if the maintainers care to answer this mystery.

hindol09:04:11

Issues page may not be for questions. I have seen some maintainers discourage that.

Jim Newton09:04:42

perhaps, but in my opinion a request to clarify documentation is a valid issue. Not everyone agrees of course.

andy.fingerhut09:04:19

You can set up Github repositories so only you can push to them. That is the default, I am pretty sure, unless you explicitly give others permission to push to them.

andy.fingerhut09:04:31

Even in repositories that multiple people can push to, those auto-generated badges are created via software running on sites like Travis and whoever created the site http://codecov.io, and to re-run automated tests, and publish the updated results, they must be run as some user on some machine somewhere. It is common for one person setting up a repo to create that setup, and then those automated jobs/tests/whatever are re-run as that configured user no matter who pushes to it.

andy.fingerhut09:04:50

But again, I am pretty sure that by default a Github repo can only be pushed to by the person who created it, not Joe blow.

andy.fingerhut09:04:31

Anybody in the world can create their own personal fork of a public Github repo, and push to their own fork, but not the original, unless the original gives them permission to do so.

Jim Newton10:04:12

@U0CMVHBL2 yes. I think with gitlab I can prevent forking. As I understand, I have to allow permission to fork. But anyone who can read can clone with a normal git clone operation. BTW, I looked at the bash script. https://codecov.io/bash and there seems to be come code to handle gitlab, but it is not clear how it works.

andy.fingerhut19:04:42

@U010VP3UY9X So I know I said this before, but if you do not care about publishing code coverage data to the world, you can completely ignore the contents of that link, and need not put anything in your README. You can use cloverage to get coverage data for yourself, and not publish it. Or you can get that coverage data and publish it by just copying and pasting the results into some file that you publish. That 'badge' link is only if you want the results automatically updated on every commit.

didibus08:04:53

But Macros are each able to do whatever they want, and you need to read their individual documentation and sometimes source code to learn how they work and what they do.

Aron08:04:32

gotcha, will check helix docs again, maybe I missed something

didibus08:04:21

But what happens, is macros get passed the code itself. So it is possible that the helix $ says that the argument it expects is a Clojure literal map. When you call ($ AssetSelectControl @state)your second argument here is a symbol to the macro, not a literal map, so that could trip up the macro.

didibus08:04:41

Well actually in this case that second argument is a list I think, because @ is a reader form which will expand to (deref state) which will then be passed to the macro as a list of two symbols

didibus08:04:07

See its doc says: > When a map literal is passed to the second argument, it will treat this map as props to provide the component and will be compiled and output as a JS object

Aron08:04:40

That starts to make sense.

didibus08:04:53

A map literal and a map are not the same thing. A map literal is the exact syntax {}, where as a map is an instance of the type PersistentMap

hindol08:04:35

Sorry to sidestep the conversation, but I will use a thread. Does a macro receive a map literal differently than a map? And if yes, does it unlock more powers?

didibus08:04:24

No, the reader will create the map from the map literal when read, and macros receive the data-structure returned by the reader.

didibus08:04:16

But the distinction still matters, because you can only pass things by value or by reference

didibus08:04:34

The value representation of a map is the map literal

andy.fingerhut08:04:39

A macro does receive (deref foo) differently than {:some-key some-expression} , even if the value of foo happens to be (atom {:some-key some-expression})

👍 4
andy.fingerhut08:04:12

A macro receives the args as the syntax you see in the macro invocation, without evaluation.

didibus08:04:15

But if you have a map instance, you need to pass it by reference, and that requires a symbol which refers to the instance, but now with a macro, the dereferencing isn't done for you, so if the macro doesn't dereference it will just get a symbol

👍 4
Aron08:04:30

Can I ask some more information on what 'reader form' is and what other forms there are? I might know what it is and just be ignorant of the terminology, but I am a bit of a loss here and this seems very important, basic stuff.

didibus08:04:49

You would need to wrap the macro in another macro that would convert the map instance back into a map literal and put that as the argument to the other macro to pass that back in as a map literal

hindol08:04:18

All this is going over my head right now but I will bookmark this conversation and come back to it later. Thanks a lot!

didibus08:04:26

I find a lot of beginner material either avoid the topic not to confuse beginners, or just do a poor job of talking about it.

didibus08:04:38

But it is fundamental to Lisps

andy.fingerhut08:04:55

@ashnur I didn't notice if anyone used the term 'reader form' specifically, but in general the text of your code is read by the Clojure reader, before it is compiled, using the function read that is built into Clojure. It is possible to experiment at the REPL to see what that does using an expression like (read-string "{:foo 2}") , where read-string is like read , except it gets its input from the string that is the first arg.

andy.fingerhut08:04:02

The link to the http://clojure.org page on the reader is one solid source of info.

Aron08:04:26

Thanks, I just shown you where I saw it mentioned since you said you didn't notice before...

andy.fingerhut08:04:29

Here is an example of behavior in the REPL that demonstrates how the reader treats the @ character when placed before a symbol:

user=> (read-string "@foo")
(clojure.core/deref foo)

Aron08:04:47

I am still catching up with earlier responses, it's much harder to understand what is written than write it 🙂

andy.fingerhut08:04:02

When that http://clojure.org reference doc page says this:

@form ⇒ (deref form)

andy.fingerhut08:04:21

You can see in my read-string REPL example above how that corresponds to the documentation, hopefully.

andy.fingerhut08:04:35

Similarly:

user=> (read-string "'foo")
(quote foo)

andy.fingerhut08:04:59

corresponds to this on the documentation page:

'form ⇒ (quote form)

Aron08:04:03

I understand most of it, yes, but I still haven't been able to put it together to use the macro properly with the map instance

Aron08:04:38

The easyness of lisp is very misleading sometimes, just because I see the code as data explicitly, doesn't mean I understand the semantic content

💯 4
andy.fingerhut08:04:02

From something that didibus said from looking at the docs, it appears that perhaps the $ macro has been written so that it expects a map literal as an argument, and doesn't behave the same way if given something that is not a map literal.

andy.fingerhut08:04:39

When I said "the docs" in my previous message, I mean the docs for helix (at least, I think that is probably what didibus was referring to -- I haven't read those docs myself)

Aron08:04:39

Yes, that's why it doesn't work as I expected, that was very clear, @U0K064KQV gives good explanations : )

didibus08:04:35

You should start thinking at 3 levels. You have the source code itself. That is a textual representation of your program. Like it's characters in a file or in a string. The text of your source code is first read by the reader, and this will parse the text and return a data-structure representation of the code. The link I gave details the logic the reader uses to parse the text into this data-structure form. For example, it says that characters between double quotes will become a String. Characters between square brackets will become a Vector. Characters between whitespace that don't start with numbers or colon will become a Symbol. Characters between whitespace that start with a colon will become Keywords. Characters between whitespace that starts with @ will become a list of two elements, the first being the symbol deref and the second the characters after the @ as a Symbol. Etc. You can use read-string to try this out. You put textual code in a string and call read-string on it and you get back the data-structure the reader generates from it. Finally, Clojure will then compile and evaluate this data-structure, using some rules about how it goes about doing that. This will turn the data-structure representation of the code into its runtime form. One of the rules here is that if there are macros contained within, they will be given a chance to modify sub-sections of the data-structure before Clojure compiles and evaluates it.

didibus08:04:26

Hard to explain more on slack. Maybe I should do a blog post on this. Cause examples would help.

👍 8
Aron08:04:52

That is incredibly useful, thanks again.

Aron08:04:35

I think I just have to do ($ ComponentName {& @state}) seems to be working, but obviously I have other bugs too

didibus09:04:25

Hum... That will set on your react Component the property & to be the map contained in state?

didibus09:04:29

Is that what you want?

Aron09:04:01

I am following this https://github.com/Lokeh/helix/blob/master/docs/creating-elements.md#dynamic-props so that's to pass dynamic props, my props are not dynamic but also not hand written, I don't really understand this terminology 🙂

didibus09:04:41

Oh ya ok, seems that's also an explicit feature of the helix $ macro

didibus09:04:02

So that won't work anywhere else FYI

didibus09:04:22

That's why with Macros, it's always all bet are off go read the doc :p

Aron09:04:11

yeah, but also helpful. I actually read that several times before I even asked the question here, but I just didn't connect the dots. You pointing out that the macro expects a 'literal' was what put things to place

hindol09:04:12

Just so everyone is aware, I just noticed there is a #helix channel as well.

Aron09:04:23

I asked there too, but no answers so far.

Aron09:04:14

I generally tend to ask the person writing the library if I can, and only come to beginners or other big channels if I really have no idea : )

didibus09:04:20

Yup, good macros can bring real convenience sometimes. Just tricky to write one and can be abused sometimes.

hindol09:04:41

Not to say, don't ask here. Shared just in case you did not know, but seems you already know about it.

Jim Newton09:04:48

I've never used spec. could someone explain the incantation to check for a given integer n and a given sequence, possibly heterogeneous, whether the sequence is of the form n many optional intergers followed by exactly n integers? some analogous expression to "a?^8a^8" which I can substitute any fixed integer for 8 ?

Aron09:04:31

I don't follow the logic. Isn't that saying that it's a sequence of integers with size between 8 and 16?

Jim Newton10:04:08

@ashnur the logic is not so important. I could substitute any other regular expression for "a" and I could insert something between a?^8 and a^8. The example comes from https://swtch.com/~rsc/regexp/regexp1.html

Jim Newton10:04:44

its an example of a regexp for which the matching is linear in grep and awk, but is exponential in pcre as well as python, java, and ruby.

Aron10:04:14

but ^ is start of the line

Jim Newton10:04:39

no, not sure what a more common syntax it, it means "match the previous regexp exactly 8 times"

Jim Newton10:04:56

I think in emacs regexp it is {8}, not 100% sure

Jim Newton10:04:31

you're right in the objection that different regexp implementations use very different semantics for the same syntax.

Jim Newton10:04:15

a?^3 is something like (a?)|(a?a?)|(a?a?a?),

Jim Newton10:04:47

BTW, I asked the same question on #clojure-spec

Aron10:04:52

as far as I understand, if you can write a function to test this, that's enough for spec

Aron10:04:35

Yeah, I asked a question there more than 24 hours ago and your question was the next message on the channel, it's been inactive. Maybe because of the holidays

Jim Newton10:04:30

it's not urgent. I'm mainly trying to determine whether spec is written to use exponential time or linear time to check regexps.

Jim Newton10:04:01

if linear time, then you can check many very complicated things extremely fast. if exponential time, then you have to avoid regexp as much as possible

didibus10:04:40

Don't think you can do that with n

didibus10:04:27

You'd need to repeat the 0 or 1 n times and then repeat the exactly n times

Jim Newton10:04:03

ahh perhaps its the same: a?a?a?a?a?a?a?a?aaaaaaaa

Jim Newton10:04:56

although if implemented the linear-time way, they turn out identical: a?|(a?a?)|(a?a?a?)|...|aaaaaaaa

Jim Newton10:04:55

and of course you cannot incorporate a variable n into the regular expression, otherwise it ceases being regular.

Jim Newton10:04:12

but for a given n we can programmatically generate the regexp

didibus10:04:32

For example, for n=2

(s/def ::foo
 (s/cat
  :0 (s/? integer?)
  :1 (s/? integer?)
  :2 integer?
  :3 integer?))

didibus10:04:13

But see, it's a bit annoying, because cat requires you to name the position, even if optional. And there are no 0 or n or exactly n, so you need to list each one.

didibus10:04:40

The reason for naming the position is because spec lets you parse this into a map as well

didibus10:04:36

(s/conform ::foo
 '(0 1 2 3))
;; => {:0 0, :1 1, :2 2, :3 3}
 
(s/conform ::foo
 '(2 3))
;; => {:2 2, :3 3}

didibus10:04:59

And to check you use valid?

didibus10:04:58

(s/valid? ::foo
 '(0 1 2 3))
;; => true

(s/valid? ::foo
 '(0 1 2 3 4))
;; => false

(s/valid? ::foo
 '(0 1))
;; => true

Jim Newton11:04:40

does interger? need to be the function or the symbol?

Jim Newton11:04:02

I tried to write a function to generate such a spec. but its difficult because the spec seems to be a macro which always sits in evaluation position. 😞

Jim Newton11:04:05

(defn gen-n [n name]
  (concat 
   (mapcat (fn [i]
             (list (keyword (format "%d" i)) `(s/? ~name))) (range n))
   (map  (fn [i]
           name) (range n))))

(defmacro match-n [n name]
  `(s/cat [email protected](gen-n n name)))

Jim Newton11:04:34

(s/valid? (match-n 8 integer?) (concat (range 8) (range 8)))

Jim Newton11:04:19

but this doesn't

(map (fn [n] (s/valid? (match-n n integer?) 
                       (concat (range n) (range n))))
     (range 10))

Jim Newton11:04:04

Basically it's difficult to treat a spec as data.

Aron12:04:09

but this is not the fault of spec, it's the nature of the problem you are trying to solve

Ben Sless13:04:35

> Basically it's difficult to treat a spec as data. That's one of the problems malli tires to solve https://github.com/metosin/malli

Jim Newton14:04:51

@UK0810AQ2 interesting.

👍 4
Jim Newton10:04:49

@UK0810AQ2 does malli address regular patterns in sequences?

Ben Sless10:04:41

There's also a slack channel for #malli

Jim Newton09:04:21

and given the spec, how can I actually check whether a given sequence is of that form?

Nathan K12:04:11

Is it possible to use a ->> chain, with join , but have the argument go in the right place?

Nathan K12:04:31

Not a lot of context, but I basically want to do this

(defn say-number
  [num]
  (->> num
       (interleave thousands-qualifiers)
       reverse
       (join " ")))

kwrooijen12:04:01

Another option is to write it like this:

(-> (interleave thousands-qualifiers num)
    reverse
    (join " "))
If you don't mind the first argument of -> being a function call.

👍 8
Nathan K12:04:06

but since the signature for join is join coll separator, this won't work ☝️

manutter5112:04:18

It’s mainly designed for the use case “I want to use threading but I have one line with the args in the wrong order”

manutter5112:04:54

lol, I was just looking that one up too 😄

Nathan K12:04:23

(as-> expr name & forms)

Binds name to expr, evaluates the first form in the lexical context
of that binding, then binds name to that result, repeating for each
successive form, returning the result of the last form.
Gee, the biggest trouble I have with learning clojure so far is the desnse documentation :D

Nathan K12:04:41

I think if I read this a few times it will make sense though.

manutter5112:04:52

Yeah, basically it’s just a version of -> that lets you assign a name to the value you want to thread so that you can put the argument wherever you want

manutter5112:04:59

tbh I never actually use it. I would just write

(defn say-number
  [num]
  (let [tmp (->> num
                 (interleave thousands-qualifiers)
                 reverse)]
    (join tmp " ")))

Nathan K12:04:11

yeah I guess I'll use the let... the as-> has me confused 😄

Nathan K12:04:51

I thought I had this right, but it's complaining that words (my tmp name) doesn't exist... my use here seems to match eaxmple on the website

Nathan K12:04:10

> unresolved symbol words

manutter5112:04:05

The post by Stuart Sierra is worth reading, and it has a link to another post specifically about as-> that’s also helpful.

manutter5112:04:08

Oh, duh, yeah, the as-> only works with ->, it breaks with ->>

yuhan12:04:02

yeah you can really only use as-> in a -> form -- to see why it's instructive to try macroexpanding the form

manutter5112:04:12

Sorry, I led you on a wild goose chase

Nathan K12:04:12

Oh Ok. Is -> the opposite of ->> ? like "thread first"?

manutter5112:04:21

Yeah, -> is thread-first

Nathan K12:04:25

:thumbsup: makes sense

manutter5112:04:33

Technically, you could do

(defn say-number
  [num]
  (as-> num $
       (interleave thousands-qualifiers $)
        (reverse $)
        (join $ " ")))

manutter5112:04:47

Put the threaded value wherever you want.

Nathan K12:04:56

ah, that's quite nifty

Nathan K12:04:12

is clojure compiled? I forget

manutter5112:04:22

It compiles down to JVM byte code

Nathan K12:04:52

but it does that sort of just in time, or as a "pre run" step?

Nathan K12:04:11

I don't even know if that question makes sense 😛

manutter5112:04:05

Yeah, it can be a bit hairy to get into the low level details. There’s an AOT flag you can set to force compilation Ahead Of Time, but that has a number of gotchas you need to be aware of before you use it. Otherwise it happens as each namespace is loaded.

manutter5112:04:43

In practice that means it happens when the program first starts running.

Nathan K12:04:19

I've been learning a few different FP languages on http://exercism.io

Nathan K12:04:39

I'm enjoying clojure and elixir the most

Nathan K12:04:58

I'm drawn to clojure mostly because I really love rich hickey talks 🙂

teodorlu13:04:15

The talks can have that effect 😁

Nathan K13:04:34

so refreshing

hindol13:04:46

And the hair.

Nathan K13:04:11

I started out liking Elm and being intrigued by haskell... Elm is quite an easy language to learn. But haskell is quite intimidating for me 🙂

introom13:04:48

How do you expand the home directory ~ ?

mister_m13:04:57

Someone else can perhaps answer - but as far as I know, you may have to replace the ~ yourself with the result of java's System.getProperty("user.home"));

introom13:04:55

yup. that’s what bothers me.

mister_m13:04:21

(clojure.string/replace "~/path/to/home/stuff"
                        #"^~"
                        (System/getProperty "user.home"))
for anyone reading (I hope this formats ok)

bartuka13:04:01

everywhere I look has something like this

manutter5113:04:37

What’s the context here? Why are you wanting to expand the home dir?

teodorlu13:04:27

´~´ is a shell convention, and your application doesn't depend on your shell. To hook back into shell features:

(require '[clojure.java.shell :refer [sh]])

  (defn bash [& args]
    (apply sh "bash" "-c" args))

  (bash "echo ~")
  ;; => {:exit 0, :out "/home/teodorlu\n", :err ""}

atrox13:04:20

Hey everyone :) Started learning clojure yesterday and converted a very simple library of mine. Just wanted to ask if this looks OK for a clojure library? Additionally, I would love to support clojurescript but I don't find any resources how to support both with such a simple library. Do I really have to duplicate the code into a .clj and .cljs? Could someone direct me to the right spot :)? Lib: https://github.com/atrox/haikunatorclj

teodorlu13:04:12

If you write .cljc-files, they will be available in both Clojure and Clojurescript.

teodorlu13:04:55

You can use reader conditionals to hook into the platform: https://clojure.org/guides/reader_conditionals

atrox13:04:21

Ouh interesting. Thank you. Didn't know about .cljc. I will try it out 🙂

teodorlu13:04:34

No problem 🙂

dabrazhe15:04:49

Hi. Is there any difference between partial and #() function? Eg

(partial mapv clojure.string/trim) vs
  #(mapv clojure.string/trim %))

shooit15:04:05

The partial version will support the 2+ arities of mapv but the anonymous version will only support the 2 arity call. When calling on a single collection, they would be identical.

shooit15:04:30

My preference is to use higher order functions such as partial or comp over anonymous functions when possible

ghadi15:04:11

I do not share that preference

💯 4
ghadi15:04:42

I tend to use anonymous functions more than partial, by a lot

ghadi15:04:49

comp is a separate concern

dpsutton16:04:25

i've heard maybe bronsa or alex mention that partial adds a weird stack trace that isn't necessary? makes finding errors harder?

ghadi16:04:45

yes, it adds a trace element from the impl of partial

dpsutton16:04:00

it seems to just be an innocent frame. I thought i remembered one/both of them mentioning a very valid point about it though

practicalli-john17:04:26

@dennisa I use partial if possible as it's cleaner code. However, if I need to reorder arguments or provide more then I'll create a custom anonymous function. Haven't noticed any performance issues with partial when testing.

👍 4
18:04:08

Hello, do any of you happen to know Packt's Clojure Workshop? It's for free right now and I'm thinking about starting it since my Clojure knowledge is close to 0

nick11:04:07

I don't know anything about Packt's Clojure Workshop but if you're considering other options - https://www.braveclojure.com/ is very nice for beginners

11:04:50

I got this ebook already, seems really good. I'm gonna start reading it today. Thanks!

tord20:04:53

I thought I knew this language pretty well, but now I suddenly can't figure out an elementary bug. In the below REPL interaction, why can't I find the key id in the map gs ?

;; gs is a map:

cljs.user> (type gs)
cljs.core/PersistentArrayMap

;; Let's inspect the keys:
cljs.user> (keys gs)
("7ngc8l")

;; OK, that one key looks like a value I have:
cljs.user> id
"7ngc8l"

;; Looks like the same thing. Let's make sure:
cljs.user> (some #(= % id) (keys gs))
true

;; Then it should be possible to look up that key in the map, right?
cljs.user> (get gs id)
nil
cljs.user> (get gs id "Doesn't exist?!")
"Doesn't exist?!"

;; ???
I'm feeling so stupid now. What am I missing?

👀 4
bfabry20:04:01

the key is in the map with a nil value

bfabry20:04:14

oh no you checked that

bfabry20:04:17

well shit 🙂

jtth20:04:58

might you need to wrap id in (keyword id)?

bfabry20:04:00

what's (type id) and (type (first (filter #(= id %) (keys gs)))

tord20:04:50

They are both strings.

bfabry20:04:39

well that is incredibly strange

noisesmith20:04:04

it's cljs, so I guess I shouldn't be surprised by anything, but I find this very surprising

noisesmith20:04:28

did some method mutate the string, js strings are mutable right?

tord20:04:06

I'm kind of happy others also find this strange. Makes me a little more confident about my sanity. Pretty sure I'm not mutating anything.

jsn20:04:17

out of curiosity, what does (get gs (-> gs keys first) :nothing) return?

tord20:04:41

That one works as expected, it returns the actual value for that key (which is too large to paste here).

noisesmith20:04:03

it could be some corner case in the strings where they look equal but they hash differently - try (map hash (keys gs)) vs. (hash id)

tord20:04:08

While (= id (first (keys gs))) returns true .

tord20:04:43

If strings are equal according to = , can they really hash differently?

noisesmith20:04:07

if somebody makes poor choices, absolutely - hash and equality are totally different code

tord20:04:19

They hash to the same thing.

tord20:04:03

"Poor choices"? We don't have any way to choose what hashing is used for Clojure strings, do we?

bfabry20:04:42

clojurescript uses javascript strings

noisesmith20:04:51

I can't think of how two Objects can be = , hash to the same value, and lookup to fail on one as the key, for any Object type whatsoever, not just strings (except NaN, NaN is the only thing I can think of that would be like this)

bfabry20:04:55

but sure, everything is changeable

noisesmith20:04:14

@U0CLH2QTV you can do weird things with object prototypes in js, methods like equality come from the prototype

tord20:04:17

Anyway, like I said, the hashes are identical.

bfabry20:04:36

something's messed with either the id or key object, the map, the get function, or some other function

tord20:04:24

Thank you all, anyway. I'll play around and see if I can solve the mystery.

👍 4
andy.fingerhut21:04:50

Can you reproduce this situation in a separate cljs REPL, out of curiosity? There is nothing special about that string value that you mention, as I can start a cljs REPL and create a map with that key, and that value for id , and successfully get the value in the map using the value of id . I do not have any suggestions for what might be going wrong other than the ones mentioned earlier in this thread.

Scott Starkey20:04:29

Hi folks - I’m learning about assoc-in wondering if there is a more Clojure-y way of doing the following:

(def states
    [ {:a false
       :b 15
       :c "Batman"}
     { :a true
       :b 15
       :c "Joker"}
     ])
  (defn flip-true [num st]
    (assoc-in (assoc-in st [0 :a] (= 0 num)) [1 :a] (= 1 num))
    )

(flip-true 0 states)
=> [{:a true, :b 15, :c "Batman"} {:a false, :b 15, :c "Joker"}]
I’m wanting a function flip-true that takes an index number (0 or 1) and a state, which makes the numbered index true, and the other false. This works! But I’m wondering if there’s a better way than nesting the assoc-in. Thoughts?

noisesmith20:04:02

in almost every case I'd use (-> st (assoc-in ...) (assoc-in ...)) instead of nested assoc-in calls

Scott Starkey20:04:20

Yes, that’s cleaner.

noisesmith20:04:59

also, updating vectors by index is something I'd only do for tuples, otherwise I'd use a hash-map with a predictable key rather than relying on order

noisesmith20:04:13

it's up to your domain whether states counts as a tuple

Scott Starkey20:04:01

OK, I wondered whether I should create a tag for each one. (I will always have just 2 for this particular use-case.)

noisesmith20:04:22

yeah, to me if there's always exactly two items, that counts as a tuple

Scott Starkey20:04:51

So, you think it should have :id 0 or :id 1 as a key? Sorry, I’m a bit of a newbie as to the term “tuple”. Looking it up now.

noisesmith20:04:26

in some languages there are first class tuples - they are disjoint combinations of values (rather than collections of arbitrary size / unified type)

noisesmith20:04:51

in clojure of course nothing has unified type, but idiomatically we use two element vectors as tuples for map key and value, for example

noisesmith20:04:14

it's less a question of how it's implemented, and more one of how it is meant to be used

believelody23:04:50

Hello everyone, is there a well documented tutorial about routing with Clojurescript/Reagent? I heard about Secretary or Reitit but just some talkshow on youtube. Thanks

athomasoriginal01:04:19

I have not see much RE tutorials, but here is a simple example of using it. https://gist.github.com/athomasoriginal/eadc022482c3432943c400cc8eeb1788 - perhaps that can help get you started 🙂

athomasoriginal01:04:17

Its a simplified version of the Reitit example in their repo.

believelody20:04:26

Thank you guys, I will check that 👍:skin-tone-6: