This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-12-18
Channels
- # adventofcode (20)
- # aleph (25)
- # announcements (4)
- # babashka (117)
- # beginners (150)
- # calva (4)
- # cider (9)
- # clj-on-windows (2)
- # clojure (9)
- # clojure-europe (4)
- # clojure-italy (3)
- # clojuredesign-podcast (18)
- # clojurescript (16)
- # conjure (1)
- # core-async (35)
- # cursive (13)
- # datalevin (6)
- # datomic (6)
- # fulcro (8)
- # hyperfiddle (5)
- # malli (8)
- # nextjournal (4)
- # off-topic (51)
- # pathom (4)
- # reagent (21)
- # sci (14)
- # shadow-cljs (22)
- # specter (3)
- # testing (22)
- # tools-deps (8)
- # xtdb (7)
Hey, I am looking for when
that evals to false (on failed check).
I can write it, but I wonder why its not common pattern.
(defn check-func? [x y]
(when (every? some? [x y])
(some-other-check? func))
Background: I am doing datetime operations on values that can be null (in db), if they are null, I just want to return false.
While for the most part this alone would work (because nil is false in most cases, I still dislike relying on nil)I understand is kinda idiomatic to rely on nil in these cases
What are x and y in your check-func?
also i would like to keep the signature with ? which should suggest true/false is returned and not nil (this is also idiomatic i believe)
(and (not-any? nil? [x y]) (some-other-check? x y))
Something like this would return false, but you probably wanted to do something more complicated in the body, that's the reason why you went for when?
If I assign the same piece of data to two variables, is the data duplicated?
I know Java is pass by reference value so the answer is no four Java, but I'm not sure about how clojure handles assignment
user=> (def data [:a :b :c])
#'user/data
user=> (def a data)
#'user/a
user=> (def b data)
#'user/b
user=> (identical? a data)
true
user=> (identical? a b)
true
user=> (identical? [:a :b :c] [:a :b :c])
false
Awesome, thank you!
I think the only time something gets implicitly copied on access or assignment would be boxing - since int/long/float/double are not Objects and can't be placed on the stack or used as keys in collections, they end up needing to be boxed which allocates a new Integer / Long / Double etc.
this is true in both java and clojure, but it's more likely to be an issue with clojure code as the compiler has less ability to detect and eliminate boxing programmatically
For immutable values, semantically by reference and by value are the same, I.e. the program behavior is the same. By reference is of course more efficient for large values like collections
Yeah I know semantically they're the same but I'm wondering about, for example, putting a piece of data into a hash map and also a vector and wondering about the memory costs of such an action
If you want to see what the in-memory representations are of Clojure data structures that are of interest to you, and whether they share data or not, my cljol library and its gallery linked from the README can help draw such pictures. It works better for small to medium sized data structures -- when they get very large usually it isn't very interesting for a person to pick through the huge drawing any longer: https://github.com/jafingerhut/cljol
Thatโs very cool! Thank you
Awesome, thank you!
I have created app using lein new re-frame routes-example +routes +10x
and after running npx shadow-cljs watch app
I am getting below issue, Has anyone got this issue ?
Is there any good way to insert large string literals in Clojure(Script) programs? I find multi-line strings inadequate for two reasons:
1. Because "
is used for both open and close for the string, I end up having to escape it with \"
, which is very irritating when inserting text with code blocks in it.
2. Since all whitespace is significant, I end up loosing my code's indentation level on all multi-line string.
While less important, also:
3. Just a personal preference, but doing (str "Some text " <some-code> " more text")
, is way more ugly when combined with #2 then, say: "some-text $<some-code> more text"
.
It might be useful to have a little more context on what you're trying to do. โข It might make sense to write your long form in a separate format like markdown and "import" it into your clj(s) code. โข it might also make sense to create a macro/function that makes things easier. โข lastly, depending on your editor, there are some extra options
I know all those options are super vague, but it does depend on what you're trying to do. There are also options like https://github.com/gdeer81/marginalia.
@U7RJTCH6J I'm trying to write a prose-heavy webpage.
I'd be up for making a macro/function, but it seems to me that what's really needed is a reader macro.
yea, there are a few literate programming tools. there are "notebook" style tools that are similar. there are documentation tools that are similar. there are also some blogging tools that are pretty much in the same space. let me find a few links.
I'm not sure I understand what you mean by columned text though
Is it for a paper?
Another option that I like, but is kinda out there is similar to your reader macro idea. For my blog, I write in markdown, but I parse the markdown myself to add extensions and control the generated html.
Some links: โข https://github.com/metasoarous/oz : notebook style, but flexible options for export โข https://github.com/gdeer81/marginalia: literate style documentation โข https://github.com/yogthos/selmer : django style templates โข https://blog.michielborkent.nl/migrating-octopress-to-babashka.html โข https://asciidoctor.org/ Some people use asciidoc for this sort of thing For this reason, I like using formats that support markup/down that are extensible so that I can output whatever I want. If you want specific outputs, it's hard to find existing options that also support those specific outputs.
Marginalia is so cool
Ya, something based on markdown is fine. I just don't want to have to write a whole thing from scratch. ๐
I do wish Marginalia documented how to use it in the 'Usage' section of the readme. (I mean, the command line flags are great, but I kind of need to see what programs written with it look like. ๐ )
The source is written in it, if that helps
Oh? That's cool. Although I don't see much in the way of prose outside of the code in the src directory.
Although I did finally get the website to not 404, and that seems to have some examples: http://gdeer81.github.io/marginalia/
fwiw, if you do decide to roll your own, the https://github.com/vsch/flexmark-java mdown parser is great. You can write a markdown->html export in not a lot of code. The https://github.com/phronmophobic/blog/blob/master/src/blog/mdown.clj#L194 is basically an escape hatch to add anything that isn't already built into markdown.
For my https://github.com/phronmophobic/blog/blob/master/src/blog/mdown.clj, there's basically blog-html
that converts a markdown AST node to hiccup.
(extend-type com.vladsch.flexmark.ast.Paragraph
IBlogHtml
(blog-html [this]
[:p {}
(map blog-html (children this))
]))
For extension, there's markdown-macro
that allows you create arbitrary extensions. So your column example could look like:
{{columns}}
{{column}}This is the first column{{/column}}
{{column}}This is the second column{{/column}}
{{column}}This is the third column{{/column}}
{{/columns}}
Here's an example blog post : https://raw.githubusercontent.com/phronmophobic/blog/master/markdown/what-is-a-user-interface.md
IMO, it was much less work to write and more flexible than most of these other options. I agree that a reader macro system would be even more handy for this type of thing.Ya, I'm currently thinking of just use scribble/text
to generate clojurescript code.
I know I could 'technically keep the indentation level from #2 if I wrote them like this:
(str "line 1 of text\n"
"line 2 of text\n"
"line 3 of text.")
But then I have to start and end every line with quotes and a \n
, which really looses the content.I guess I'm looking for some sort of literate string programming embedded in clojure(script)
Something like pollen: https://docs.racket-lang.org/pollen/
Can the file names of clj source files not contain hyphens? It looks like when :require
-ing a lib in my project, it failed because the file it was looking for was my_lib.clj
on the classpath instead of the my-lib.clj
I had in the project. The namespace is defined as my-lib
. This fix (renaming my file) is simple but Why are underscores required?
Maybe another loaded question: I find myself with a map of a similar structure to the following thing you might use to hold a "point" on a Cartesian plane: {:x 2 y: 0}
. I find myself thinking that I want to define a defrecord
for this structure, and maybe make some use of defmulti
to make explicit that certain fns I am defining take a "point" and not just a map that looks like a point. Is that "idiomatic" in clojure or is just passing around a map that looks like a point preferred until it isn't
There is a place to reach for those but I wouldn't start there. Stay with functions and maps until you find a good reason to do it another way. In your example these can include polymorphic operations in different coordinates systems, or performance
@radicalmatt I would say, while you're learning Clojure, try to avoid reaching for the "comfort" of OOP style code.
Idiomatic Clojure is, for the most part, about open maps and functions that accept inputs that can grow and are not tied to "types".
it feels pretty naked at first, like something important is missing, but eventually you get used to it and it's freeing
You might also want to look at Spec as a way to describe the shape of maps you are working with, so you could write an fdef
for your point-taking functions partly as documentation but also partly as a debugging aid since you can instrument
your code and then the system will check that your function is being passed something like looks like a "point" hash map.
how would i go about doing sequential operations idiomatically in clojure? specifically I'm trying to work on day 18 advent of code, i have [[[[5,5],0],1],[[[0,0],1],[[0,6],[0,9]]]]
for example, and i wish to find maximum depth by going each character, incrementing if it's [
, decrement if ]
Not sure what you mean by "sequential", but take a look at loop
and recur
. For an iterative form.
well, it doesn't appear to work
i tried calling recur inside when but it doesn't let me
and if i try to define a symbol based on a condition, it out of scope at point when i call recur
I don't know how to make this work
i can't
then i get an error it can only be at tail
(ns day-18.core
(:gen-class))
(def lines (clojure.string/split-lines (slurp "input.txt")))
(def first-line (nth lines 0))
(defn is-opening-bracket?
"Checks if character is opening bracket"
[character]
(= character \[))
(defn is-closing-bracket?
"Checks if character is closing bracket"
[character]
(= character \]))
(defn find-max-depth
"Finds maximum depth of snailfish number"
[snailfish-number]
(loop [char-index 0
depth-list (list 0)]
(if (= char-index (count snailfish-number))
(println depth-list)
(do
(println depth-list)
(let [current-char (nth snailfish-number char-index)]
(println current-char)
(when (is-opening-bracket? current-char)
(recur (inc char-index) (conj depth-list (inc (nth depth-list 0)))))
(when (is-closing-bracket? current-char)
(recur (inc char-index) (conj depth-list (dec (nth depth-list 0))))))
))))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(find-max-depth "12346"))
(defn find-max-depth
"Finds maximum depth of snailfish number"
[snailfish-number]
(loop [char-index 0
depth-list (list 0)]
(if (= char-index (count snailfish-number))
(println depth-list)
(do
(println depth-list)
(let [current-char (nth snailfish-number char-index)]
(println current-char)
(cond
(is-opening-bracket? current-char)
(recur (inc char-index) (conj depth-list (inc (nth depth-list 0))))
(is-closing-bracket? current-char)
(recur (inc char-index) (conj depth-list (dec (nth depth-list 0))))
:else
(recur (inc char-index) depth-list)))))))
(defn find-max-depth
"Finds maximum depth of a properly balanced string with brackets"
[expression]
(loop [chars (seq expression)
current-depth 0
max-depth 0]
(if (empty? chars)
max-depth
(if (is-opening-bracket? (first chars))
(recur (rest chars) (inc current-depth) (max (inc current-depth) max-depth))
(if (is-closing-bracket? (first chars))
(recur (rest chars) (dec current-depth) max-depth)
(recur (rest chars) current-depth max-depth))))))
so how can you use recur multiple times
is when literally the only construct with this issue?
well thats annoying
(defn find-max-depth-helper
[chars current-depth max-depth]
(cond
(empty? chars)
max-depth
(is-opening-bracket? (first chars))
(find-max-depth-helper (rest chars)
(inc current-depth)
(max (inc current-depth) max-depth))
(is-closing-bracket? (first chars))
(find-max-depth-helper (rest chars)
(dec current-depth)
max-depth)
:else
(find-max-depth-helper (rest chars)
current-depth
max-depth)))
(defn find-max-depth
"Finds maximum depth of a properly balanced string with brackets"
[expression]
(find-max-depth-helper (seq expression) 0 0))
so โrecurโ is a hint you can give to the compiler that you donโt need to maintain the whole stack
(defn find-max-depth-helper
[chars current-depth max-depth]
(cond
(empty? chars)
max-depth
(is-opening-bracket? (first chars))
(recur (rest chars)
(inc current-depth)
(max (inc current-depth) max-depth))
(is-closing-bracket? (first chars))
(recur (rest chars)
(dec current-depth)
max-depth)
:else
(recur (rest chars)
current-depth
max-depth)))
(defn find-max-depth
"Finds maximum depth of a properly balanced string with brackets"
[expression]
(find-max-depth-helper (seq expression) 0 0))
you can still do โnon-tailโ recursion, it just will have a callstack like all normal code
i get that
why cant i just do normal jumps
normal for loop
iterative
The names loop
and recur
often trip up beginners to Clojure because those words often mean very different things in other languages. One way to think about loop
and recur
in Clojure is that loop
sets up local iteration "variables" and provides their initial values. When you call recur
, you are doing the moral equivalent of starting another iteration of the loop and providing the "updated" values of the iteration variables. Of course, in reality, the "variables" are really just local symbols which are bound to immutable values in each iteration. Also remember that loop
is an expression in Clojure and returns a value. In most other non-LISP languages a loop
is a statement which only does control flow and does not return anything.
> why cant i just do normal jumps Because the language does not support that, for better or worse
but most problems you solve with clojure wont be advent of code and you wont feel that absence as much
strong disagree about using loop / recur here, the initial problem statement has you consuming a sequence in order left to right, that is what reduce
is for
(def input
"[[[[5,5],0],1],[[[0,0],1],[[0,6],[0,9]]]]")
(first (reduce (fn [[maximum current] token]
(cond (= token \[)
[(max maximum (inc current)) (inc current)]
(= token \])
[maximum (dec current)]
:else
[maximum current]))
[0 0]
input))
the other solutions need to replicate the "examine input one token at a time" logic that reduce has built in
loop is low level and only suitable for consuming collections in order for performance reasons (in practice, that means pretty much never as that's so rarely the bottle neck)
back to the initial question:
> how would i go about doing sequential operations idiomatically in clojure?
collection -> collection: look at map / filter / remove / mapcat / for etc.
collection -> side effect: doseq, run!
collection -> arbitrary result: reduce (with the reduced
function to make it stop early if needed)
hey i'm interested in learning clojure and trying to get set up on windows. whats the difference between building the clojure jar myself vs the clj-on-windows ps script?
I'd assume ps script is easier, if you build your own clojure jar, you have to setup a new PATH and all manually
@ingrown.potato You can get a lot of detailed feedback on the approaches available for Windows in the #clj-on-windows channel. My setup is using WSL2 (Ubuntu) and running everything on there but with VS Code on Windows and the remote-wsl2 extension.
If you really want to stay with Powershell, I'd suggest using Scoop per the wiki (which is where I assume you learned about the PS script).
@seancorfield thank you. i didn't see that channel before.
also, it seems like building the clojure will only yield clojure.jar but the docs say theres clojure and clj. will building it give me both?
You don't need to worry about the JAR. The CLI (`clojure` and clj
) are shell scripts (bash on Linux/macOS, PS on Windows).
Think of the JAR files as "just libraries" that your program will depend on.
(no one ever needs to build Clojure themselves, unless they're actually going to be a contributor to it, rather than a user)
(I actually use brew
on WSL2 so I can easily install any version and upgrade/downgrade easily, just like the macOS experience)
No idea how far you'll get with msys2. Unless it's improved a lot since the last time I tried it.
the only caveat is for my use case, i need to be able to compile class files that are loaded in another program.
You mean, reuse Clojure code in a non-Clojure program?
my use case is, i have a separate program that loads java class files to build extensions. i've been interested in clojure for a while so i thought i'd give it a shot.
I would suggest looking at Clojure's Java API and at most writing a Java class wrapper for your entry point and really stick with Clojure in source form and work with it in its "normal" REPL-based mode.
That how we integrated Clojure into our legacy apps, a decade ago, and continue to work with that today.
@ingrown.potato One of the nice things about this approach is that your host program, running on the JVM, can dynamically load arbitrary Clojure code at any point, while it is running, and if you start it with a JVM option to open a Socket REPL (built into Clojure), then you can connect from an editor and inspect and modify the program while it is running.
We sometimes apply patches to our legacy production apps this way, via a REPL, from our editor, while it is running -- with zero downtime.
The AOT compilation process is fraught with pitfalls, especially if you're trying to use it with "library" code to be honest.
clojure.java.api
is part of Clojure itself (the three JAR files that make up the core of Clojure). The clj
and clojure
scripts depend on libraries, including Clojure.
The scripts are just how you run stuff. Your program is separate from those scripts and has full access to the whole Clojure (and Java) ecosystem.
@seancorfield thank you very much for your help!