This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-04-10
Channels
- # announcements (2)
- # babashka (20)
- # beginners (381)
- # calva (16)
- # chlorine-clover (21)
- # cider (1)
- # clj-kondo (18)
- # cljs-dev (107)
- # cljsrn (2)
- # clojure (161)
- # clojure-dev (5)
- # clojure-europe (2)
- # clojure-losangeles (3)
- # clojure-nl (3)
- # clojure-uk (35)
- # clojurescript (22)
- # conjure (48)
- # cursive (8)
- # datascript (11)
- # datomic (6)
- # figwheel-main (10)
- # fulcro (85)
- # hoplon (112)
- # jobs (1)
- # kaocha (5)
- # luminus (5)
- # malli (1)
- # off-topic (96)
- # onyx (1)
- # pedestal (1)
- # perun (1)
- # reagent (5)
- # reitit (1)
- # shadow-cljs (34)
- # specmonstah (1)
- # sql (6)
- # tools-deps (3)
- # xtdb (23)
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))
duh, thanks!
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?
@ordoflammae You would provide a comparison function that includes that logic.
I'm still trying to figure out comparison functions; how would I do that?
You're already sorting by some function, yes?
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.
What's your key-fn?
(I assume it's computing the sum of digits in some value in the data?)
(defn weight
[s]
(reduce + (map char->int s)))
char->int
is a function that converts a character to an integer.
Change it to return a two element vector that has the weight as the first element and the value as the second element.
Oh, that's a good idea.
Thanks.
(if you want a numeric comparison, you'll need to parse it to long)
That worked! Thanks @seancorfield!
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
Welcome @brettkromkamp!
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
Let's see how that goes 🙂
Cool. You may find that Luminus has too many moving parts as a starting point, although the book Web Development with Clojure is good.
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
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.
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
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
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. 😞
@jings.bill bidi
is another possibility for data-driven routing. We use that for one app at work (and compojure
for the rest).
@seancorfield Thanks again! I'll take a look at the resources you linked to
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
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).
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
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.
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...
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….
Hey, Clojure is "just" a library so it's easy to add as a dependency to an existing Java app... 🙂
We actually started of using Clojure as a library for some low-level stuff inside our existing ColdFusion apps running on the JVM!
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
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.
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.
'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.
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.
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.
That’s, ahhh…. quite the unconventional path. 🙂
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)
@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).
Using (comment ...)
to hold on to useful code snippets to inspect a running system
REPL is a misnomer. We’ll probably never get rid of it, but it gives a false notion of the development style
Live development of a program in memory, but none of the image nonsense
Yeah, good analogy -- Smalltalk was pretty radical.
@seancorfield well, thanks for correcting me on that false belief : )
Stu talks about that in a couple of presentations -- he's always a bit shocked when he sees people actually typing into a REPL.
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 : )
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.
I wish I could use REBL, I joined the patreon, downloaded it, tried once, never had time again.
I have a few videos up on YouTube showing how I use Atom/Chlorine/REBL.
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.
@ashnur Editors are very subjective. There's a plugin for VS Code that is based on Chlorine and works with Socket REPLs.
There's a Socket REPL integration for neovim too I believe. Conjure.
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.
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.
As a Clojure editor, though... emacs (spacemacs or prelude) is a mind-blowing experience (at least for me it is) 🙂
more than 4 years ago https://github.com/syl20bnr/spacemacs/commit/60d38ffdd2414919a59458305b34b1f1d2f37acc#diff-04c6e90faac2675aa89e2176d2eec7d8R281
I used Emacs for several years before switching to Atom 🙂
(like, back in the 17.x, 18.x days)
@seancorfield Off the top of your head... what makes Atom a better Clojure experience than emacs? Genuinely interested...
@brettkromkamp I talk about about it in this post https://corfield.org/blog/2018/12/19/atom-chlorine/
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.
On my team, we're like 10 people, and we have folks using Eclipse, Gedit, Emacs, Spacemacs, Vim, IntelliJ
We don't really have any issues, everyone just uses what they prefer, it doesn't really matter much
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
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.
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.
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 🙂
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.
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.
@seancorfield Thanks. I'll check out the blog post
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.
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.
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.
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.
I used to recommend Eclipse and Counterclockwise, but the latter isn't maintained anymore unfortunately.
Next up would probably be Atom + Chlorine, and then VSCode/Calva and finally Emacs/Cider or Vim/VimIced
I think Atom+Chlorine while it has less features, has a pretty easy barrier for entry
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.
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 ; )
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>
nope.
> For Symbols, syntax-quote resolves the symbol in the current context, yielding a fully-qualified symbol (i.e. namespace/name or fully.qualified.Classname).
so `(and) is not referencing clojure.core/and ?
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.
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.
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>
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?
i confess to not being able to explain it in detail -- what i know comes from reading LispReader.java
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 ~@others))]
(if then1
`(if ~if1 (do ~@then1) ~extra-clauses#)
`(or ~if1 ~extra-clauses#)))))
@jimka.issy in the Doc "The test-constants are not evaluated." so (and) in test is not clojure.core/and but just a symbol
@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?
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...
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
@andy, what do you mean by namespace qualified? Do you mean interned into *ns*
? in the cl-cond
macro
`(if ~if1 (do ~@then1) ~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 ?user=> `if
if
user=> `the
user/the
user=> `foo
user/foo
user=> `print
clojure.core/print
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.
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.
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
user=> (resolve 'if)
nil
user=> (resolve 'print)
#'clojure.core/print
user=> (resolve 'the)
nil
user=> (resolve 'foo)
nil
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.
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.
you're right. just trying to understand. sorry if my questions seem like complaints. I appreciate the engineering that has gone into clojure.
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"
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.
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, ....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.
As I understand nil
is java null
, not ()
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
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"
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.
The design of Clojure is heavily influenced by Scheme and CL, but it is in no way bound by them.
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.
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.
can you cljs->js
it and use js tools you’re more familiar with to display it?
@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.
surely using ansi is not the only way to display the string result of value serialization in a formatted way?
the idea here is that the off the shelf pretty printers don't know anything about html, but they do emit ansi
though depending on what you need, a pre
tag plus pprint should suffice
I had to read the docs https://github.com/greglook/puget#syntax-coloring puget appears to support html too
https://clojurians.slack.com/archives/C053AK3F9/p1586542646418100?thread_ts=1586525965.396600&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 : )
pprint takes a writer as an optional second argument
the writer can just create a string or whatever
(binding [*print-right-margin* 80]
(with-out-str (pprint value)))
this will wrap at 80chars
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.
and Bill, that's an interesting tidbit, but my trouble is really not the lack of wrapping : )
I’m probably not being helpful because I don’t really understand what your goal is
you want to display an arbitrary data structure in HTML somehow?
At the moment I am just trying to start my dev env which broke somehow and I don't even understand how
(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"
(that's using clj / jvm of course...)
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? :thinking_face:But when I did it told me that com.mysql.jdbc.Driver
was deprecated and that I should use com.mysql.cj.jdbc.Driver
Am I correct to be surprised by this?
user=> (clojure.string/split "xxxyxxx" #"x")
["" "" "" "y"]
see https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#split-java.lang.CharSequence- - just using Java's behavior here
"Trailing empty strings are therefore not included in the resulting array."
You can give an additional -1 arg to split t get the trailing empty strings, if you prefer that behavior.
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?)
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
this is subjective / speculative though
unlike the others, core.matrix is constructed with the intention that any other matrix lib in the future could be dropped into it
I see
so if my intentions are to start and doing ML with clojure, I better not to
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
thanks
@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".
Thank you @seancorfield 🙂
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
Thank you! http://cljdoc.org has made it a lot easier to write documentation for projects 🙂
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
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.
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.
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.
I was wondering if there was something in the standard library. I ended up rolling my own.
So if list a => [1 2 3 4 1001], b => [1 2 3 4], the result is true.
But if list a => [1 2 3 4 1001], b => [1 2 2 3 4], the result is false.
No, it doesn't.
I’m just playing around on the repl — how do I get a newline in a string? something like (prn "hello \n world")
That's pretty much it, why?
i guess, that doesn’t print with the newline in the repl
prn
won’t, you have to use println
It's probably because of how prn
works; use println
.
lol i thought prn
was just shorthand for println
. Guess not. TIL
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?try bumping the async version to to 1.1.587 and clojure to 1.10.1 in the project.clj file
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 [
]
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.
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
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
(in particular, clj-new
so that it's easy to create an entire new project with tests etc)
(That's what cause the nth not supported
error @christopher.joslyn
(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.6Another 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.
(yeah, you'd need to downgrade Leiningen to an older version in order to run against Clojure 1.6)
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.
That seems sensible. It occurred to me I might want old tools around for old projects. Wasn’t sure if that would be recommended.
isn't that basically an atom but waiting to run the function once rather than several times after it may have changed?
volatile, as a jvm construct, is a thing that ensures changes to some variable are published between threads
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)
in a true multithreaded context where multiple threads are writing to a volatile at once, vswap! is a race condition
it means one threading doing things to a volatile, then handing it off to another thread
I would not trust a usage of volatile outside of clojure.core without sitting and thinking about it
and as demonstrated in the nrepl code, instead of realizing their mistake using volatile! they doubled down and used volatile + a lock
(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)
You might be able to assoc-in
a vector.
Yes, you should be able to assoc-in
a vector.
It's a little bit of an odd trick, but vectors behave like maps where the keys are just the indexes.
So with assoc-in i would be able to assign a new key to every single map in the vector?
Yeah, map might work better if that's what you're trying to do.
I thought you were just trying to assoc
a certain map.
Yeah, you want map
for that..
map
will apply a function to every value in a sequence.
user=> (for [item [{:a 1} {:b 2}]] (assoc item :x 1))
({:a 1, :x 1} {:b 2, :x 1})
user=>
Equivalent using map:
(map #(assoc % :x 1) [{:a 1} {:b 2}])
Yup, sorry, I was hand-crafting my code on the spot.
It's not the same for
, so it might just confuse you.
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
Yeah, that's why it's confusing.
It's very similar to map
, but with a few more abilities.
At least, from what I understand.
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.
yeah, even though while practicing i worked with for, i fell right into the trap just minutes ago
somehow though, the language is really about working with the data and yet that seems the hardest to me to grasp
like in Python/Pandas or Ruby, I just get the json from the API, put it in a Hash / Data format
Yeah, it definitely requires a whole new way of thinking.
another reason to prefer map
over for
: it plays well with ->>
It helps when I think about Clojure as imperative programming, but turned inside out.
Or, instead of thinking top-down, in Clojure you think inside-outside.
beware: once you get used to it, you may find yourself never wanting to mutate a collection ever again
@jings.bill, I can testify to that.
There's more.
Here's an article I found helpful on threading macros.
http://www.spacjer.com/blog/2015/11/09/lesser-known-clojure-variants-of-threading-macro/
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->
”
some->
has saved me from several nil
checks when I wasn't sure if my request had succeeded.
;; 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 $)))
;; ->> 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)))
I didn't know that str/join
was preferred over apply str
.
It does make more sense...
here is the link to the tutorial if you would like to see more: https://grison.me/2020/04/04/starting-with-clojure/
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 🙂
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
watching e.g. videos of eric normand solving a simple data problem was useful, for example
Have you checked out https://www.braveclojure.com/?
i’d see someone use partition-by
, for example, and think “oh that is way easier than what I was doing”
i did see some awesome functions that might be useful as some point, like the one you mentioned partition-by
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
some other tricks I had to review and think about
however now i want to have the key & value that are added based on working with a key from each map
Could you rephrase that?
{: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"}
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.
So your anonymous function would look something like #(assoc % (operation %))
.
Yeah.
Actually what I put there won't work.
But you could get it to work with a little tweaking.
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)
Yeah, @seancorfield's idea is probably better.
Yeah, pretty much.
(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
(edited to have the assoc
when there is a preview image instead of when there is not)
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.
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’
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.
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)