Fork me on GitHub
#beginners
<
2020-04-10
>
scotto00:04:31

OK, I’m trying to figure out what I’m doing wrong with the map function. Can you help? I’ve defined the following:

(def b-state
  [ { :x 15, :init nil }
    { :x 15, :init nil } ] )
I’d like to come up with a function to give “init” to one of the two (either 0 or 1), and make the other one nil. I tried this, and it doesn’t work…
(defn give-init [num state]
      (map (assoc-in state [% :init] (= num %)) [0 1]))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: % in this context,
However, I can plug the assoc-in and this works:
(assoc-in b [0 :init] (= 0 0))
(assoc-in b [1 :init] (= 0 1))

zdot10100:04:03

it looks like you're missing a # in front of (assoc-in

scotto00:04:24

duh, thanks!

ordoflammae01:04:46

Is there a way to sort with secondary comparators? For example, I want to sort primarily by the sum of the digits of a number, but if that comparison returns equal, I want to do a normal greater than or less than comparison. How can I do this idiomatically?

seancorfield01:04:07

@ordoflammae You would provide a comparison function that includes that logic.

ordoflammae01:04:39

I'm still trying to figure out comparison functions; how would I do that?

seancorfield01:04:53

You're already sorting by some function, yes?

ordoflammae01:04:10

Yeah. I'm using sort-by right now, but that's only able to sort by one criteria, I want a secondary criteria, but I can't figure out how to implement a sorting function right.

seancorfield01:04:36

What's your key-fn?

seancorfield01:04:23

(I assume it's computing the sum of digits in some value in the data?)

ordoflammae01:04:27

(defn weight
  [s]
  (reduce + (map char->int s)))

ordoflammae01:04:39

char->int is a function that converts a character to an integer.

seancorfield01:04:13

Change it to return a two element vector that has the weight as the first element and the value as the second element.

ordoflammae01:04:25

Oh, that's a good idea.

seancorfield01:04:32

(if you want a numeric comparison, you'll need to parse it to long)

ordoflammae01:04:24

That worked! Thanks @seancorfield!

brettkromkamp04:04:21

Hi all! Have been playing around with the idea to learn Clojure for a long time (I'm a Python developer)... taking my first real steps and learning Luminus

seancorfield04:04:05

Welcome @brettkromkamp!

brettkromkamp04:04:04

@seancorfield Thanks!

brettkromkamp04:04:11

The plan is to build a simple REST API for an existing (Python) application... so, trying to get a feeling for Clojure web development with a small Luminus application

brettkromkamp04:04:18

Let's see how that goes 🙂

seancorfield04:04:47

Cool. You may find that Luminus has too many moving parts as a starting point, although the book Web Development with Clojure is good.

seancorfield04:04:37

If you want to look at something that's a bit simpler than Luminus as a way to explore a basic web app, see if this helps https://github.com/seancorfield/usermanager-example

seancorfield04:04:56

As a Pythonista, you may want to check out https://github.com/clj-python/libpython-clj as a way to call into Python from Clojure.

jings.bill04:04:01

I’ve been rolling my own. I don’t know anything at all about the web stack (I’m an Android developer by trade), so I figured that would be the most entertaining way to explore things

jings.bill04:04:13

So far I’ve picked up ring for the server, compojure for routing, hiccup for templating, and etaoin to drive the browser for local previews

jings.bill04:04:56

the only thing I’ve learned from that is that, while I like the simplicity of compojure, I think that my need to programmatically generate paths will lead me to reit in the future at some point. but reit was really tough to grok when I was first wiring everything together. 😞

seancorfield05:04:06

@jings.bill bidi is another possibility for data-driven routing. We use that for one app at work (and compojure for the rest).

brettkromkamp05:04:07

@seancorfield Thanks again! I'll take a look at the resources you linked to

jings.bill05:04:27

Putting everything together myself has been a whole lot of fun, but the moment when I had the whole thing up and running and tweaking the running code live through an REPL…. well that was when I really started getting addicted to working on the thing

seancorfield05:04:30

Yup. The live development via the REPL. That's the secret sauce.

seancorfield05:04:53

We run socket REPLs in a lot of our processes, on QA and production as well, and so I can connect my editor direct to those processes and work live inside the running applications for debugging (and patching, if I have to).

jings.bill05:04:47

I do wonder if I could ever apply some of this to my work in Android. Unfortunately we’re a little resource constrained on the device there

jings.bill05:04:48

The development loop there is just a bear. Especially in a mature codebase, compile->deploy can be a couple of orders of magnitude longer than I’m experiencing right now.

seancorfield05:04:16

I have seen a demo of a Clojure app running on Android with a REPL to modify the app live... it was a conference talk a few years ago...

jings.bill05:04:37

Hmm. It’d be a tough sell to develop in clojure at a larger company, but even just getting a REPL up and running would be a big win for debugging….

seancorfield05:04:24

Hey, Clojure is "just" a library so it's easy to add as a dependency to an existing Java app... 🙂

seancorfield05:04:37

We actually started of using Clojure as a library for some low-level stuff inside our existing ColdFusion apps running on the JVM!

ashnur05:04:27

I used devcards, not a repl. I could never understand the repl stufff. I do like the immediate feedback, but for me input is my source files not some ephemeral session

didibus06:04:30

Oh, you really should practice some more with the REPL workflow, it's quite transformative, a bit of a multiplier for productivity in my opinion. I think well worth the learning curve.

ashnur08:04:27

Do you give me lessons? Are you prepared to answer silly questions from me? I am afraid people forget that other people do not - in general - have the same amount of time to spend on the same things they've spent their time on. Clojure is generally a community where you find people who really had the time to invest into learning the tooling. In contrast with javascript for example where you don't need to know anything, just use create-react-app. Now, I have to add that this is not true anymore for clojure either, there are many template creator tools that are similar to cra, but it used to be the case even just 4-5 years ago.

ashnur08:04:59

'well worth the learning curve' assumes that I have the resources to spend on it. IT also assumes that if I don't learn something, it's not because I haven't had the time, but because I choose to do so.

didibus21:04:23

I mean in the sense that the time spent learning it is paid back in time savings on added productivity afterwards. At least it was for me. And I'm talking only within the context of doing Clojure work.

ashnur04:04:15

That is totally understood. What I am saying is that in order to something to be "paid back" something needs to be paid first, in this case time, that I don't have.

jings.bill05:04:32

That’s, ahhh…. quite the unconventional path. 🙂

jings.bill05:04:36

I had to pick up some of the REPL stuff from others… I started with the content in Clojure for the Brave and True, saw a talk from Stuart Halloway in another place (i think probably from Sean here) that pointed to some other ideas (key among them: bias heavily towards writing code in a source file and eval-ing from there, rarely if ever running directly at the prompt)

seancorfield05:04:42

@ashnur My input is my source files. I don't type into a REPL -- I type into my source files and eval that code (into the, possibly remote, REPL).

seancorfield05:04:58

Never type into a REPL! 🙂

jings.bill05:04:26

Using (comment ...) to hold on to useful code snippets to inspect a running system

jings.bill05:04:07

REPL is a misnomer. We’ll probably never get rid of it, but it gives a false notion of the development style

jings.bill05:04:42

It’s really more like driving a smalltalk VM off of code in source files

jings.bill05:04:26

Live development of a program in memory, but none of the image nonsense

seancorfield05:04:43

Yeah, good analogy -- Smalltalk was pretty radical.

ashnur05:04:12

@seancorfield well, thanks for correcting me on that false belief : )

seancorfield05:04:04

Stu talks about that in a couple of presentations -- he's always a bit shocked when he sees people actually typing into a REPL.

ashnur05:04:59

well, the tooling is impossibly hard to grasp unless you actually use the jvm for anything, which i don't. Even now I am able to actually do some work with clojurescript because mostly I rely on shadow-cljs : )

seancorfield05:04:03

I will occasionally drop a completely ephemeral expression into a REPL if all I need is a quick doc lookup or validation of what some function does, but I use Cognitect's REBL as my REPL engine so it has a full visual history of all evaluated expressions.

ashnur05:04:12

so that's my defense for the misunderstanding : D

ashnur05:04:39

I wish I could use REBL, I joined the patreon, downloaded it, tried once, never had time again.

seancorfield05:04:01

I have a few videos up on YouTube showing how I use Atom/Chlorine/REBL.

ashnur05:04:17

I might've seen them if they are not too recent ; )

ashnur05:04:06

but I don't know chlorine, and I will not use Atom, this thing with the editors has been getting on my nerves since 2008. I tried everything. I mean every editor. I bet you can't say one I haven't tried. And I dislike all of them for different reasons. Right now I use vscode when I have to work with other people's code and neovim for my own code. Maybe this is offtopic, sorry if inappropriate.

seancorfield05:04:31

@ashnur Editors are very subjective. There's a plugin for VS Code that is based on Chlorine and works with Socket REPLs.

seancorfield05:04:55

There's a Socket REPL integration for neovim too I believe. Conjure.

ashnur05:04:06

I like conjure too, but not everything supports prepl yet

seancorfield05:04:17

prepl builds on top of the Socket REPL so the latter is the bottom line. I'd like to see Chlorine use prepl but it works great with just Socket REPL.

ashnur05:04:24

thanks for the clarification, I should remember. Although there are soo many little details that are complicating everything, I am sure I will forget most everything I picked up in the past couple of days by next week.

ashnur05:04:34

like how to not console.log

brettkromkamp05:04:36

As a Clojure editor, though... emacs (spacemacs or prelude) is a mind-blowing experience (at least for me it is) 🙂

jake14221:04:24

Ha! Can’t tell you how many times I’ve laughed at that quote.

seancorfield05:04:09

I used Emacs for several years before switching to Atom 🙂

seancorfield05:04:21

(like, back in the 17.x, 18.x days)

brettkromkamp05:04:49

@seancorfield Off the top of your head... what makes Atom a better Clojure experience than emacs? Genuinely interested...

seancorfield05:04:02

@brettkromkamp I talk about about it in this post https://corfield.org/blog/2018/12/19/atom-chlorine/

ashnur05:04:11

heh, what do you think about the following theory. By 2020 there are soo many developers working on so many different things that they can't sync up, so whatever editor they are using, sometimes people need to switch to work with colleagues so each editor has some number of distinct groups of software people who use it in some specific context... Which means that depending on what context you want to work in, you might or might not need a different editor. Almost everyone uses vscode for typescript.

didibus06:04:58

On my team, we're like 10 people, and we have folks using Eclipse, Gedit, Emacs, Spacemacs, Vim, IntelliJ

didibus06:04:23

We don't really have any issues, everyone just uses what they prefer, it doesn't really matter much

didibus06:04:37

I kinda just learned how to use them all, it's not that hard, it's mainly the shortcuts are a bit different, but it's not like when you take over your coworkers keyboard/mouse you're going to be doing a big session, normally you just type a few things and you're done

ashnur07:04:38

You haven't mentioned what I was talking about -> what context do these people work in? If my theory is correct, people using the same editors, or editors from the same age group, work mostly on the same things.

didibus21:04:31

My team does Clojure/Java/Scala on micro-services

ashnur04:04:15

Everyone is working on the same thing, and only one thing? Again, the point of the question is to see if people who work on the same things use the same (or at least not everyone something else) editors.

didibus05:04:13

Hum.. what do you mean by same thing? We all work on the same set of systems yes.

didibus05:04:36

That means the same code base

ashnur13:04:34

I literally mean the same files, same lines. I think I also made the exception of older languages (C, but also Java to some degree) being from a time where all languages were supported by all editors, and Clojure definitely is supported by any editor that supports java, so it was never a problem to choose an editor for clojure itself. More the issue with using a REPL and so forth. But I digress 🙂

didibus17:04:26

Hum... Sometimes we do work on the same line, but not at the same time, unless we're doing pair programming or something like that.

didibus17:04:45

But ya, I don't know about JS, maybe editor support is weak, and so you're restricted to only certain editors. We have a bit of JS, but only to add a few interactive features to some internal web pages we maintain, so we don't really need much in terms of IDE support for it.

brettkromkamp05:04:14

@seancorfield Thanks. I'll check out the blog post

ashnur05:04:23

emacs is one of these old editors that do not get new stuff ported too often, while atom and vscode and neovim do get new stuff all the time. So if you work on old things, these new editors are not very stable. But if you work on new things, you need the features of the new editors.

didibus06:04:45

Why do you say that? Emacs pretty much has everything ported to it. I think it might be the editor with the most amount of features and support for the most languages to be honest.

seancorfield05:04:35

Emacs has plugins for pretty much everything under the sun and CIDER/nREPL/clj-refactor are very actively developed -- but that blog post talks about why I switched to a more modern editor. And VS Code is fine. I have it installed on all my machines but it's not as customizable as Atom -- by design -- so it would be hard for me to switch since so much of my workflow is based on customization that I apply to Atom at startup that would be very hard to apply to VS Code.

seancorfield05:04:23

But, as I said, editors are extremely subjective. Some people love Emacs, some people hate it. It really doesn't matter, as long as your favorite editor has a strong REPL integration so you can develop a really productive "RDD" workflow.

didibus06:04:32

For beginners, personally, I'd really recommend IntelliJ with Cursive

didibus06:04:05

I used to recommend Eclipse and Counterclockwise, but the latter isn't maintained anymore unfortunately.

didibus06:04:05

Next up would probably be Atom + Chlorine, and then VSCode/Calva and finally Emacs/Cider or Vim/VimIced

didibus06:04:37

I think Atom+Chlorine while it has less features, has a pretty easy barrier for entry

didibus06:04:48

So it's a good place to start with

pez07:04:04

My recommendation to beginners is to stick with their favorite editor and install Clojure REPL tooling in it. Biased as I am, if that editor is VS Code, I think Calva is your best choice, and I will work hard for it to be that.

ashnur07:04:06

One thing is sure, everyone can find an editor for their liking to write clojure in it. (I just wish this was true for other languages too ; )

jimka.issy10:04:47

Can someone help me understand why this fails:

clojure-rte.core> (case (first `(and 2 3 4))
                    (and) 100)
Execution error (IllegalArgumentException) at clojure-rte.core/eval25749 (form-init16272114045093011175.clj:37874).
No matching clause: clojure.core/and
but this one succeeds
clojure-rte.core> (case (first `(and 2 3 4))
                    (clojure.core/and) 100)
100
clojure-rte.core> 

sogaiu10:04:36

does this help?

user=> `and
clojure.core/and

sogaiu10:04:09

> For Symbols, syntax-quote resolves the symbol in the current context, yielding a fully-qualified symbol (i.e. namespace/name or fully.qualified.Classname).

jimka.issy10:04:51

so `(and) is not referencing clojure.core/and ?

sogaiu11:04:29

no, i think i was mistaken here. under ordinary circumstances, `(and) should be referencing clojure.core/and. if you (def and 1), it won't be true though, iiuc.

sogaiu10:04:00

think that's right -- if you look at the docs for case: https://clojuredocs.org/clojure.core/case

(case e & clauses)

Takes an expression, and a set of clauses.
 Each clause can take the form of either:
 test-constant result-expr
 (test-constant1 ... test-constantN)  result-expr
 The test-constants are not evaluated.

jimka.issy10:04:32

So apparently I can do the following to make it resolve correctly?

clojure-rte.core> (case (first `(~'and 2 3 4))
                    (and) 100)
100
clojure-rte.core> 

sogaiu10:04:15

that seems like it should work

jimka.issy10:04:27

So how does it work if it is a macro and the and symbol needs to resolve to clojure.core/and Is there a post processing step after macro expansion which moves symbols to the clojure.core namespace?

sogaiu10:04:09

i confess to not being able to explain it in detail -- what i know comes from reading LispReader.java

jimka.issy10:04:03

For example, I'm using the following macro, and (if ...) does not resolve to the local namespace, rather it resolves to clojure.core/if`, right?

(defmacro cl-cond  [[if1 & then1] & others]
  
  (when (or if1 then1 others)
    (let [extra-clauses# (if others `(cl-cond [email protected]))]
      (if then1
        `(if ~if1 (do [email protected]) ~extra-clauses#)
        `(or ~if1 ~extra-clauses#)))))

sogaiu10:04:05

that seems correct

gon11:04:59

@jimka.issy in the Doc "The test-constants are not evaluated." so (and) in test is not clojure.core/and but just a symbol

jimka.issy12:04:35

@gon, I really don't understand. Of course the test-constants are not evaluated, but how is that related to how a macro or backquote expands? Doesn't expansion happen before compilation?

gon14:04:08

sure but arguments in macro are not evaluated, so (and) in test happens to slip through unevaluated, the opposite happens for the expo part, which is evaluated and results in a namespaces qualified symbol, at the end the compare is between the symbol and and the clojure.core/and, at the least this is my understanding...

andy.fingerhut14:04:13

Backquoted aka syntax-quoted expressions cause symbols to be namespace qualified by default, unless you do something to prevent that. The case constants in your example were not syntax quoted so they did not get replaced with namespace qualified versions

jimka.issy11:04:15

@andy, what do you mean by namespace qualified? Do you mean interned into *ns*? in the cl-cond macro

`(if ~if1 (do [email protected]) ~extra-clauses#)
the if becomes clojure.core/if but in
`(and 2 3 4)
the and becomes my.local.ns/and ... I don't understand under which circumstances they are treated differently ?

andy.fingerhut13:04:20

user=> `if
if
user=> `the
user/the
user=> `foo
user/foo
user=> `print
clojure.core/print

andy.fingerhut13:04:02

There may be some differences here between Clojure and what you are accustomed to in CL (Common Lisp). In CL, the reader interns symbols in packages during read time. I do not have a quick explanation at my mental fingertips to explain the difference precisely, but I am pretty sure there is no interning of symbols at all in Clojure, at any time, read time or otherwise.

andy.fingerhut13:04:09

Note that if does not become clojure.core/if - It is one of a very few special forms in Clojure that seems not to be associated with a namespace explicitly at all.

andy.fingerhut13:04:51

Symbols are resolved at some time in Clojure (not sure precisely when, but I believe it is closer to eval time than read time), via clojure.core/resolve

andy.fingerhut13:04:54

user=> (resolve 'if)
nil
user=> (resolve 'print)
#'clojure.core/print
user=> (resolve 'the)
nil
user=> (resolve 'foo)
nil

andy.fingerhut13:04:50

These differences between Clojure and CL are by design, and have what Rich Hickey considers are benefits when writing macros in Clojure, versus writing macros in CL.

andy.fingerhut13:04:49

I am not the best person to rattle off those benefits and differences off the top of my head, since I haven't spent lots of time thinking through the subtleties. I say this mainly to emphasize that these differences between Clojure and CL are by design, designed by someone who knew CL's mechanisms better than I do, and wanted something different.

andy.fingerhut13:04:42

@jimka.issy ^^^

jimka.issy13:04:17

you're right. just trying to understand. sorry if my questions seem like complaints. I appreciate the engineering that has gone into clojure.

andy.fingerhut13:04:04

I don't take it as complaints, but as the confusion that occurs when you are using something where it is maddeningly familiar, except when it differs, and there is no road warning sign telling you explicitly "you have just started doing something different from CL"

andy.fingerhut13:04:58

Unless you go out of your way to prevent it, every Clojure namespace, including user that you are often in by default in a REPL (but you can change that of course) require's clojure.core in such a way that no clojure.core/ prefix is required, nor is any other prefix required. So resolve of any symbol that has a Var def'd in clojure.core will resolve to that Var in clojure.core. resolve of symbols that have no namespace qualifier in front of them, if it is not in clojure.core, nor any other namespace that has been require'd in that way, will either resolve to a Var def'd locally in the current namespace, or will fail to resolve.

jimka.issy15:04:47

Well its also a bit of a shock when clojure works differently than every other lisp, not just common lisp. A case when I saw recently was that in every other lisp when you use nil as the matcher in case, nothing matches, but in clojure nil matches nil .

(case nil
  nil 100)
this evaluates to 100 in clojure, but would fail in every other lisp that I know. scheme, emacs lisp, common lisp, ....

jimka.issy15:04:20

because the 2nd nil is taken as a list of possible matching values, an empty list. but in clojure it seems nil is not the empty list.

jimka.issy15:04:47

As I understand nil is java null, not ()

andy.fingerhut21:04:36

Right, that is a design decision in Clojure by Rich Hickey. I mentioned a talk he gave in 2008 to a group of Lisp enthusiasts (primarily CL and Scheme), and he mentions a lot of these differences. He doesn't always spend lots of time explaining each difference, but in many cases mentions them, and gives some motivation for the difference. https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/ClojureIntroForLispProgrammers.md

andy.fingerhut21:04:09

More than any other source I know, that talk is a good set of road signs of "Hey, Scheme/CL user, here are things you are familiar with, but here are a lot of differences, too"

andy.fingerhut21:04:56

The empty list is not nil, and neither is the empty set nil, and neither is the empty map nil. Each of those types of collections has its own distinct empty value, and they are not = to each other.

andy.fingerhut21:04:55

The design of Clojure is heavily influenced by Scheme and CL, but it is in no way bound by them.

andy.fingerhut21:04:23

Even in 2008 when Rich Hickey gave that talk, there were already CL implementations running on the JVM, e.g. Armed Bear CL: https://common-lisp.net/project/armedbear/. He explicitly did not want to create another implementation of CL, for many reasons he describes.

ashnur12:04:06

I have some value I would like to display in a react component in a way that reveals it's nature - not just identifies it. So I thought I would look for pretty print but that prints to the console. I spent like 2 hours googling how to have some random map or vector and recursively print it to html/react and I have no idea, I have not got closer to this goal despite finding soooooo many things about pprint, but it's incredibly complicated, and archaic like common list formatters. I see I could pretty print to the console, but that's not enough here, I can't ask people for this usecase to open their console.

ryan07213:04:14

can you cljs->js it and use js tools you’re more familiar with to display it?

wxitb201713:04:25

@ashnur so there is lib that converts cljs data structure to ANSI (like zprint or puget), and there is pretty many js libs to render ANSI colors in the dom. Just combine the two and you shall get what you want.

ashnur18:04:51

I don't understand this. Isn't ansi about terminals? All what I do is in the browser.

ashnur18:04:40

surely using ansi is not the only way to display the string result of value serialization in a formatted way?

noisesmith18:04:58

the idea here is that the off the shelf pretty printers don't know anything about html, but they do emit ansi

noisesmith18:04:26

though depending on what you need, a pre tag plus pprint should suffice

ashnur18:04:03

I had to read the docs https://github.com/greglook/puget#syntax-coloring puget appears to support html too

ashnur18:04:16

will check it now, thanks for pointer Lucy Wang

ashnur18:04:18

https://clojurians.slack.com/archives/C053AK3F9/p1586542646418100?thread_ts=1586525965.396600&amp;cid=C053AK3F9 this is actually where I started looking, because it didn't suffice. PPRINT prints to the console, not returns a value. I found with google with-out-str but it's just a simple line, with pre all I get is a horizontal scroll, not an improvemement : )

noisesmith18:04:27

pprint takes a writer as an optional second argument

noisesmith18:04:40

the writer can just create a string or whatever

jings.bill18:04:19

(binding [*print-right-margin* 80]
      (with-out-str (pprint value)))

jings.bill18:04:26

this will wrap at 80chars

ashnur18:04:52

I saw in the docs that there is the writer but the docs itself is completely opaque to me at this point. I have no idea how to use the information in here: http://cljs.github.io/api/cljs.pprint/ The examples often don't even contain references to the command pprint, so it's definitely not for me to understand them.

ashnur18:04:15

and Bill, that's an interesting tidbit, but my trouble is really not the lack of wrapping : )

ashnur18:04:50

like, that now just looks like how to use these dynamic variables the doc mentions?

jings.bill18:04:44

I’m probably not being helpful because I don’t really understand what your goal is

jings.bill18:04:56

you want to display an arbitrary data structure in HTML somehow?

ashnur18:04:01

At the moment I am just trying to start my dev env which broke somehow and I don't even understand how

ashnur18:04:12

then I will try to use puget if it works

noisesmith19:04:20

(ins)user=> (with-open [w (.StringWriter.)]
               (pprint {:a (range 10) :b (range 10 20)} w)
               (str w))
"{:a (0 1 2 3 4 5 6 7 8 9), :b (10 11 12 13 14 15 16 17 18 19)}\n"

noisesmith19:04:43

(that's using clj / jvm of course...)

ashnur19:04:21

I am in a browser : )

mr.liberg14:04:43

Hi! I’m struggling to connect to a mysql database via Google’s cloud sql proxy.

;;; snippet from project.clj
  :dependencies [[org.clojure/clojure "1.10.1"]
                 [seancorfield/next.jdbc "1.0.409"]
                 [mysql/mysql-connector-java "8.0.19"]]

;;; snippet from core.clj
(ns admin.core
  (:require [next.jdbc :as jdbc]))

(def ds (jdbc/get-datasource {:dbtype "mysql"
                              :dbname "talarlistan"
                              :username "root"
                              :password ""
                              :classname "com.mysql.jdbc.Driver"}))
(jdbc/execute! ds ["select * from organizations"])
But I get:
Execution error (SQLException) at java.sql.DriverManager/getConnection (DriverManager.java:702).
No suitable driver found for jdbc:
What might be the problem? 🤔

gon14:04:17

did you try to remove the classname option ?

mr.liberg14:04:36

I hadn’t reloaded my repl

mr.liberg14:04:16

But when I did it told me that com.mysql.jdbc.Driver was deprecated and that I should use com.mysql.cj.jdbc.Driver

mr.liberg14:04:33

Then I removed the classname option

mr.liberg14:04:17

Also it’s :user not :username

rs14:04:07

Am I correct to be surprised by this?

user=> (clojure.string/split "xxxyxxx" #"x")
["" "" "" "y"]

alexmiller14:04:13

"Trailing empty strings are therefore not included in the resulting array."

andy.fingerhut15:04:15

You can give an additional -1 arg to split t get the trailing empty strings, if you prefer that behavior.

aviv16:04:07

hey, what would be the 'right' tool for matrix calculation tasks (later used for ML), I see mikera/core.matrix OR uncomplicate.neanderthl and libpython-clj (then use numpy). what will be the 'future' of data/ml use with clojure? (which tools does the community prefer and thus should I pick?)

noisesmith17:04:54

very few people use python via clojure, so I think that's ruled out. core.matrix is extensible and semi-official so I think it's more likely to stick around, it has multiple implementations to choose from

noisesmith17:04:04

this is subjective / speculative though

noisesmith17:04:01

unlike the others, core.matrix is constructed with the intention that any other matrix lib in the future could be dropped into it

aviv20:04:32

so if my intentions are to start and doing ML with clojure, I better not to

noisesmith20:04:11

I don't know about that, but you'll more likely have success with a Java API rather than Python if any such thing exists

seancorfield16:04:41

@mr.liberg If you run into any further problems with next.jdbc or SQL-related stuff in general, there's a #sql channel where I'm more likely to see a "cry for help".

mr.liberg16:04:02

Thank you @seancorfield 🙂

potetm16:04:57

Oh yeah. I got the chance to glance through the docs for next.jdbc the other day, and it looks really good @seancorfield. A drastic improvement over java.jdbc

seancorfield17:04:30

Thank you! http://cljdoc.org has made it a lot easier to write documentation for projects 🙂

potetm16:04:10

Looking forward to hopefully using it one day.

potetm16:04:17

Great work.

jtth18:04:35

does anyone know of an easy way to use tailwindcss in server-side clj (selmer templates)? I really don’t want to pull in npm or cljs or shadow-cljs or whatever, I just want to use some TailwindUI components in a simple server-side webapp

jtth18:04:39

ah, thanks! on the site they caution against including the whole thing because they include a lot of stuff and having some kind of parser to snip out only what you use and serve that locally is sort of the idea behind their whole deal, or at least that’s what the site says, but maybe this is more straightforward, or i’m misunderstanding something.

ashnur18:04:02

if you want to select only part of it you need something to build with and that means nodejs anyway to run your tooling in it.

jtth18:04:15

yeah, true. just realizing that. thank you for your help!

ashnur18:04:18

unless someone already published parts of tailwind separately which I haven't found

ashnur18:04:28

I am sorry I couldn't be of more help

ordoflammae19:04:46

Is there a way to test if one list is a subseq of another? I can't use sets because I need to make sure that all duplicates in list b are also duplicated in list a.

jumar20:04:48

My first naive approach would be:

ordoflammae20:04:46

I was wondering if there was something in the standard library. I ended up rolling my own.

ordoflammae19:04:43

So if list a => [1 2 3 4 1001], b => [1 2 3 4], the result is true.

ordoflammae19:04:02

But if list a => [1 2 3 4 1001], b => [1 2 2 3 4], the result is false.

ashnur19:04:54

does order matter

ordoflammae19:04:21

No, it doesn't.

ryan07219:04:08

I’m just playing around on the repl — how do I get a newline in a string? something like (prn "hello \n world")

ordoflammae19:04:34

That's pretty much it, why?

ryan07219:04:48

i guess, that doesn’t print with the newline in the repl

manutter5119:04:04

prn won’t, you have to use println

ordoflammae19:04:08

It's probably because of how prn works; use println.

ryan07219:04:38

lol i thought prn was just shorthand for println. Guess not. TIL

hindol.adhya19:04:28

prn serializes (also pr, and pr-str). print and println prints.

christopher.joslyn20:04:56

TLDR: What’s a good way to get a repl going for a project using an old clojure version? Here are some specifics. Take for example this project: https://github.com/ericnormand/lispcast-clojure-core-async. So that’s clojure 1.6 and an alpha version of core.async. Try it with lein first. Here’s my lein env:

> lein version
Leiningen 2.9.3 on Java 11.0.6 OpenJDK 64-Bit Server VM
All my plugins for the user profile in ~/.lein/project.clj are commented out. Here’s what happens to me:
> lein do clean, install, repl
...blah blah...
Error loading nrepl.server: java.lang.RuntimeException: Unable to resolve symbol: volatile! in this context, compiling:(nrepl/middleware/print.clj:78:17)
Exception in thread "main" java.lang.ClassNotFoundException: nrepl.server, compiling:(/private/var/folders/m0/cnyn59810vs4m4lvt97c1fwc0000gp/T/form-init6359026770174126093.clj:1:1883)
Ok so let’s try clj. Here are the dependencies:
> more deps.edn
{:deps
 [{org.clojure/clojure {:mvn/version "1.6.0"}}
  {org.clojure/core.async {:mvn/version "0.1.346.0-17112a-alpha"}}]}
I’ve tried running this a few different ways but here’s the simplest:
> clj
Error building classpath. nth not supported on this type: PersistentArrayMap
java.lang.UnsupportedOperationException: nth not supported on this type: PersistentArrayMap
What’s a repl-hungry developer to do?

dpsutton20:04:50

try bumping the async version to to 1.1.587 and clojure to 1.10.1 in the project.clj file

seancorfield20:04:57

Your deps.edn format isn't quite right -- try:

{:deps
 {org.clojure/clojure {:mvn/version "1.6.0"}
  org.clojure/core.async {:mvn/version "0.1.346.0-17112a-alpha"}}}
No [ ]

christopher.joslyn20:04:36

You know, I don’t know if it was just me, but it was oddly less than easy to find an example of multiple deps in the clojure documentation.

christopher.joslyn20:04:50

And to be clear, I never found an example.

seancorfield20:04:24

Good point. The only example I can find is in the CLI reference docs, way down the page, just above this link https://clojure.org/reference/deps_and_cli#_paths

christopher.joslyn20:04:01

Nice! Saving that. Thanks.

seancorfield20:04:16

And if you go down the CLI/`deps.edn` path, you'll want to check out some of the tools listed here https://github.com/clojure/tools.deps.alpha/wiki/Tools

seancorfield20:04:55

(in particular, clj-new so that it's easy to create an entire new project with tests etc)

seancorfield20:04:19

(That's what cause the nth not supported error @christopher.joslyn

dpsutton20:04:44

(defn volatile!
  "Creates and returns a Volatile with an initial value of val."
  {:added "1.7"
   :tag clojure.lang.Volatile}
  [val]
  (clojure.lang.Volatile. val))
volatile was added after 1.6

dpsutton20:04:49

and is used in nrepl in lein

andy.fingerhut20:04:53

Another possibility: I have found that using Leiningen version 2.8.1 succeeds on that project with lein repl command, with no errors at least to the point of getting to a REPL prompt, and no changes to the files in the project.

seancorfield20:04:01

(yeah, you'd need to downgrade Leiningen to an older version in order to run against Clojure 1.6)

hiredman20:04:00

nrepl uses a volatile, and then wraps it in locking

andy.fingerhut20:04:33

I have about 8 older versions of lein command installed in my standard dotfiles, mainly because they are small, very occasionally useful like this, and take more time to remove than to keep around: https://github.com/jafingerhut/dotfiles/blob/master/bin/lein-2.8.1. I am sure there is a different URL that could be used to get older versions in an official place.

christopher.joslyn20:04:24

That seems sensible. It occurred to me I might want old tools around for old projects. Wasn’t sure if that would be recommended.

dpsutton20:04:44

isn't that basically an atom but waiting to run the function once rather than several times after it may have changed?

hiredman20:04:11

volatile, as a jvm construct, is a thing that ensures changes to some variable are published between threads

hiredman20:04:25

volatile the clojure thing is built on that jvm construct

hiredman20:04:16

it mainly exists to provide a fast mutability mechanism for transducers, that ensures changes are published across threads (incase the transducer is used a multithreaded context like core.async)

hiredman20:04:52

that model is basically linear usage, but spread across multiple threads

hiredman20:04:02

and in that context it is safe

hiredman20:04:55

in a true multithreaded context where multiple threads are writing to a volatile at once, vswap! is a race condition

dpsutton20:04:56

what does linear usage mean here?

hiredman20:04:18

it means one threading doing things to a volatile, then handing it off to another thread

hiredman20:04:26

only ever a single thread at a time

hiredman20:04:08

if you see code in the wild using volatile! it is usually a bad sign

dpsutton20:04:21

outside of a transducer you mean?

dpsutton20:04:31

or even then?

hiredman20:04:18

I would not trust a usage of volatile outside of clojure.core without sitting and thinking about it

dpsutton20:04:45

interesting. thanks

hiredman20:04:49

and as demonstrated in the nrepl code, instead of realizing their mistake using volatile! they doubled down and used volatile + a lock

hiredman20:04:16

very not not #beginners like

seancorfield20:04:20

(well, y'all know my feelings about nREPL and Leiningen -- being "easy" rather than "simple" and not good for beginners when things go wrong, which they often do)

seancorfield20:04:22

^ not #beginners topics -- perhaps go to a thread?

patrick.glind22:04:10

I've grabbed a json from an api using clojure.data.json and that went well so far

patrick.glind22:04:31

Now i have a data structure consisting of a vector containing a lot of maps

patrick.glind22:04:45

I would like to add an extra key to all these maps

patrick.glind22:04:05

Google search tells me that it's best for me to convert all these to a map of maps

patrick.glind22:04:38

Then it would be easy with assoc-in

ordoflammae22:04:49

You might be able to assoc-in a vector.

patrick.glind22:04:01

For some reason as a beginner working with the data structures is hard for me :(

ordoflammae22:04:07

Yes, you should be able to assoc-in a vector.

ordoflammae22:04:24

It's a little bit of an odd trick, but vectors behave like maps where the keys are just the indexes.

patrick.glind22:04:39

So with assoc-in i would be able to assign a new key to every single map in the vector?

hiredman22:04:44

you may just want to use map

ordoflammae22:04:56

Yeah, map might work better if that's what you're trying to do.

hiredman22:04:06

or even for

ordoflammae22:04:07

I thought you were just trying to assoc a certain map.

patrick.glind22:04:19

Every single map in the vector

patrick.glind22:04:26

I want to add a key/value

ordoflammae22:04:27

Yeah, you want map for that..

ordoflammae22:04:41

map will apply a function to every value in a sequence.

patrick.glind22:04:48

Map the function or map the data structure?

hiredman22:04:53

user=> (for [item [{:a 1} {:b 2}]] (assoc item :x 1))
({:a 1, :x 1} {:b 2, :x 1})
user=>

patrick.glind22:04:38

For looks tempting because of my background haha

ordoflammae22:04:39

Equivalent using map:

(map #(assoc % :x 1) [{:a 1} {:b 2}])

ordoflammae22:04:39

Equivalent using map:

(map #(assoc % :x 1) [{:a 1} {:b 2}])

patrick.glind22:04:13

one small correction that worked in the REPL:

patrick.glind22:04:20

(map #(assoc % :x 1) [{:a 1} {:b 2}])

ordoflammae22:04:39

Yup, sorry, I was hand-crafting my code on the spot.

ordoflammae22:04:03

It's not the same for, so it might just confuse you.

hiredman22:04:05

for is doing the same thing you would do with map there, for is just more convenient if you don't have a function already written for mapping

patrick.glind22:04:07

Yeah i prefer the no loop to maximize my learning and undoing of previous habits

hiredman22:04:14

for is not a loop

ordoflammae22:04:34

Yeah, that's why it's confusing.

ordoflammae22:04:21

It's very similar to map, but with a few more abilities.

patrick.glind22:04:31

Thanks guys :) i will try right away

ordoflammae22:04:32

At least, from what I understand.

patrick.glind22:04:20

i will try it both ways

ordoflammae22:04:25

Just make sure you don't confuse Clojure for with the typical for of other languages; it'll be really confusing. That's what I have trouble with.

patrick.glind22:04:55

yeah, even though while practicing i worked with for, i fell right into the trap just minutes ago

patrick.glind22:04:58

so many ways to achieve many things

patrick.glind22:04:48

somehow though, the language is really about working with the data and yet that seems the hardest to me to grasp

patrick.glind22:04:17

like in Python/Pandas or Ruby, I just get the json from the API, put it in a Hash / Data format

patrick.glind22:04:19

and work with it

patrick.glind22:04:29

with Clojure i’m currently still thinking

patrick.glind22:04:35

okay, i can do (first) etc

patrick.glind22:04:04

however i do feel that once it ‘clicks’ a whole new world will open up to me ha

ordoflammae22:04:33

Yeah, it definitely requires a whole new way of thinking.

jings.bill22:04:48

another reason to prefer map over for: it plays well with ->>

ordoflammae22:04:11

It helps when I think about Clojure as imperative programming, but turned inside out.

ordoflammae22:04:51

Or, instead of thinking top-down, in Clojure you think inside-outside.

jings.bill22:04:38

beware: once you get used to it, you may find yourself never wanting to mutate a collection ever again

patrick.glind22:04:54

haha yeah i’m looking forward to those moments

ordoflammae22:04:55

@jings.bill, I can testify to that.

patrick.glind22:04:11

i played a bit with -> and ->>

patrick.glind22:04:16

and then i got stuck on some function

patrick.glind22:04:24

this was some time ago

patrick.glind22:04:48

and couple of days ago i learned about as->

patrick.glind22:04:06

i felt pretty stupid not finding that earlier ha

ordoflammae22:04:21

There's more.

ordoflammae22:04:34

Here's an article I found helpful on threading macros.

patrick.glind22:04:56

great reference, thanks!

patrick.glind22:04:13

i tried some once without the -> but it didn’t ‘click’ yet

jings.bill22:04:42

there’s a lot of stylistic corners to the threading macros… the rule of thumb i’ve found that’s served well has been “use -> on instances, ->> on collections, and consider whether i’m abusing threading if I have to use as->

ordoflammae22:04:46

some-> has saved me from several nil checks when I wasn't sure if my request had succeeded.

patrick.glind22:04:52

from a tutorial I got this code:

patrick.glind22:04:58

;; as-> macro version
(defn shout [text]
  (as-> (seq text) $
    (handle-empty $)
    (interpose \  $)
    (concat $ (repeat 3 \!))
    ;; (apply str $) ; flagged by kibit
    (str/join $)
    (str/upper-case $)))

patrick.glind22:04:16

and as an exercise i wanted to rewrite it as ->>

patrick.glind22:04:22

;; ->> macro version
;; (defn shout [text]
;;   (->> text
;;        (seq)
;;        (handle-empty)
;;        (interpose \ )
;;        (add-explamation-marks) ; replacement for concat, due to thread-last macro
;;        (apply str) ; was not flagged by kibit...
;;        (str/upper-case)))

patrick.glind22:04:55

had to rewrite the concat statement in a function to ensure the working of ->>

patrick.glind22:04:10

and found out as seen in the comment that kibit wasn’t complaining about (apply str)

ordoflammae22:04:45

I didn't know that str/join was preferred over apply str.

ordoflammae22:04:58

It does make more sense...

patrick.glind22:04:51

here is the link to the tutorial if you would like to see more: https://grison.me/2020/04/04/starting-with-clojure/

patrick.glind22:04:29

although i’m beyond the point of installing Clojure and firing up the IDE/REPL there were still some interesting new things i found in there 🙂

jings.bill22:04:27

I worked out of Clojure for the Brave and True. But even after that there’s a lot of just… seeing people apply different tricks

jings.bill22:04:41

watching e.g. videos of eric normand solving a simple data problem was useful, for example

jings.bill22:04:41

i’d see someone use partition-by, for example, and think “oh that is way easier than what I was doing”

patrick.glind22:04:52

yes i did, but eventually got lost

patrick.glind22:04:14

i also watched a couple of the Apropos video’s with Erik Normand

patrick.glind22:04:27

and was like wow this is going a little to fast for me atm

patrick.glind22:04:10

i did see some awesome functions that might be useful as some point, like the one you mentioned partition-by

jings.bill22:04:45

that’s fine if it all goes by past… if I hadn’t been trying to solve the exact same problem as him, i probably wouldn’t have had the light bulb go off

jings.bill22:04:53

some other tricks I had to review and think about

patrick.glind22:04:52

follow up question

patrick.glind22:04:25

i was able to work with map to add a key & value to the vector of maps

patrick.glind22:04:14

however now i want to have the key & value that are added based on working with a key from each map

ordoflammae22:04:28

Could you rephrase that?

patrick.glind22:04:29

{:description nil,
 :has_access false,
 :content_id 12345,
 :downloadable false,
 :title "Daily Routine 12345",
 :id 67890,
 :subject_id 3747327,
 :position 1,
 :x 1,
 :preview_image
 "example.jpg",
 :download_url "",
 :subject
 {:duration "38:43",
  :preview_image_url
  "example",
  :versions {:hls ""},
  :subtitles []},
 :created_at "2020-04-10T00:42:09.167-04:00",
 :chapter_type "video"}

patrick.glind22:04:34

here is an example map in the vector

patrick.glind22:04:47

as you can see, doing the suggestion from above, by applying map

patrick.glind22:04:52

there is now :x 1 in the map

patrick.glind22:04:10

now instead of doing :x 1

patrick.glind22:04:28

i want to find out if there is an :preview_image that is not nil

patrick.glind22:04:45

and if it is not nil, create the new key and value based on that

ordoflammae22:04:06

OK, so remember that the map exists in the % variable in the anonymous function; so pass that to another function that does the operation you want.

patrick.glind22:04:15

(map #(assoc % :x 1) json-result)

patrick.glind22:04:25

this i need to update to accomodate this

patrick.glind22:04:39

so % contains the 1 map with all it’s keys & values

ordoflammae22:04:57

So your anonymous function would look something like #(assoc % (operation %)).

patrick.glind22:04:22

yeah i was going to say i probably have to replace :x 1 with a function to work on %

ordoflammae22:04:23

Actually what I put there won't work.

ordoflammae22:04:31

But you could get it to work with a little tweaking.

seancorfield23:04:06

You might find it easier to write a function that accepts a single map and returns an updated version of it -- and test that on its own -- and then you can (map my-transform json-result)

patrick.glind23:04:40

so what you are saying is: 1. (first json-result)

ordoflammae23:04:46

Yeah, @seancorfield's idea is probably better.

patrick.glind23:04:54

2. build a function that manipulates that map to the right output

patrick.glind23:04:05

then 3. apply / change it to work for all maps in the vector

ordoflammae23:04:12

Yeah, pretty much.

seancorfield23:04:10

(defn my-transform [item]
  (if (:preview_image item)
    (assoc item ...)
    item))

(map my-transform json-result)
Easier to read and reason about than trying to do too much with an anonymous function inline in the map

patrick.glind23:04:48

loving this feedback, learning a lot here from all of you 🙂

seancorfield23:04:02

(edited to have the assoc when there is a preview image instead of when there is not)

seancorfield23:04:44

One of the nice things about Clojure is that if you can break your work down into a series of small transformation, step-by-step, then it's easy to compose those and apply them to more complex data structures.

patrick.glind23:04:34

yeah that’s one of the things why i keep coming back to Clojure every time hoping that it will ‘click’ in my mind so I can ‘see the Matrix’

patrick.glind23:04:31

do you guys prefer:

(defn function-name [args])
or (defn function-name [args]) ?

patrick.glind23:04:43

i see it being used mixed

noisesmith23:04:12

I only use the former if the whole function fits on one line. Since the doc string belongs between the function name and args, I put a line break there.

patrick.glind23:04:01

Thanks @seancorfield & @ordoflammae, it worked! I was able to follow the steps for 1 item, then turn it into a map operation with a function for all items (maps)

patrick.glind23:04:14

and got the result i was looking for