Fork me on GitHub
#beginners
<
2021-12-18
>
rmxm13:12:16

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)

Antonio Bibiano13:12:37

I understand is kinda idiomatic to rely on nil in these cases

Antonio Bibiano14:12:35

What are x and y in your check-func?

rmxm14:12:19

values that can be either datetime or nil and some-other-check must receive datetime

rmxm14:12:51

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)

Antonio Bibiano14:12:02

(and (not-any? nil? [x y]) (some-other-check? x y))

Antonio Bibiano14:12:39

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?

rmxm14:12:57

yeah looks, ok thanks

Ben Sless15:12:05

Since they're assumed to be date time or nil you can (and x y (check ,,,))

nice 2
rmxm17:12:13

there's always a nicer expression 🙂

😄 1
NoahTheDuke16:12:21

If I assign the same piece of data to two variables, is the data duplicated?

NoahTheDuke16:12:44

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

Ben Sless16:12:18

By reference 🙂

sundarj16:12:13

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

noisesmith19:12:16

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.

noisesmith19:12:10

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

👍 1
emccue16:12:09

no, not unless its an unboxed primitive

andy.fingerhut16:12:44

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

NoahTheDuke16:12:53

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

andy.fingerhut18:12:10

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

👍 1
NoahTheDuke18:12:23

That’s very cool! Thank you

popeye16:12:25

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 ?

Romit Gandhi17:12:30

Run command npm install

🙌 1
👍 1
Benjamin18:12:17

is there an idiomatic naming for a map? e.g. foo->bar

jumar18:12:47

That's what Zach suggests in Elements of Clojure

2
leif18:12:01

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" .

phronmophobic19:12:44

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

phronmophobic20:12:40

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.

😍 1
leif20:12:25

@U7RJTCH6J I'm trying to write a prose-heavy webpage.

leif20:12:30

Basically documentation.

leif20:12:56

Markdown is good for purely linear text, but less good for, say, columned text.

leif20:12:41

I'd be up for making a macro/function, but it seems to me that what's really needed is a reader macro.

leif20:12:15

I get a 404 when I click on the link you sent.

leif20:12:46

Ah, nvm, it's just that slack mangled the link into a different one.

phronmophobic20:12:18

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.

phronmophobic20:12:44

I'm not sure I understand what you mean by columned text though

phronmophobic20:12:52

Is it for a paper?

leif20:12:02

No, its for a website.

leif20:12:06

Let me grab an example.

leif20:12:33

Would be hard to do with markdown.

leif20:12:53

(At least without dropping down into html)

phronmophobic20:12:26

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.

leif20:12:24

Okay, so basically a pseudo-reader maacro.

leif20:12:38

Okay, thanks anyway. 🙂

phronmophobic20:12:56

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.htmlhttps://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.

NoahTheDuke21:12:51

Marginalia is so cool

leif23:12:40

Ya, something based on markdown is fine. I just don't want to have to write a whole thing from scratch. 😉

leif23:12:46

Anywa, thanks for the links.

leif23:12:27

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. 😉 )

leif23:12:32

Anyway, thanks again. 😄

NoahTheDuke23:12:44

The source is written in it, if that helps

leif23:12:31

Oh? That's cool. Although I don't see much in the way of prose outside of the code in the src directory.

leif23:12:50

Although I did finally get the website to not 404, and that seems to have some examples: http://gdeer81.github.io/marginalia/

phronmophobic23:12:47

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.

leif00:12:09

Ya, I'm currently thinking of just use scribble/text to generate clojurescript code.

👍 1
leif00:12:17

That way I can have clojurescript+reader macros. 🙂

metal 1
leif19:12:19

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.

leif19:12:41

I guess I'm looking for some sort of literate string programming embedded in clojure(script)

leif19:12:48

(For writing long form prose).

mister_m19:12:21

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?

mister_m19:12:09

Did some more googling and the answer is JVM interoperability reasons.

mister_m20:12:10

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

Ben Sless21:12:29

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

seancorfield20:12:49

@radicalmatt I would say, while you're learning Clojure, try to avoid reaching for the "comfort" of OOP style code.

seancorfield20:12:20

Idiomatic Clojure is, for the most part, about open maps and functions that accept inputs that can grow and are not tied to "types".

Cora (she/her)20:12:37

it feels pretty naked at first, like something important is missing, but eventually you get used to it and it's freeing

seancorfield20:12:44

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.

Filip Strajnar22:12:34

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 ]

dorab22:12:38

Not sure what you mean by "sequential", but take a look at loop and recur. For an iterative form.

Filip Strajnar22:12:08

well, it doesn't appear to work

Filip Strajnar22:12:19

i tried calling recur inside when but it doesn't let me

Filip Strajnar22:12:39

and if i try to define a symbol based on a condition, it out of scope at point when i call recur

Filip Strajnar22:12:56

I don't know how to make this work

emccue23:12:32

ah so don’t use def

emccue23:12:45

instead recur with both things you need to do

Filip Strajnar23:12:30

then i get an error it can only be at tail

emccue23:12:33

can you copy paste the code so its easier for me to edit?

Filip Strajnar23:12:43

(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"))

emccue23:12:04

(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)))))))

emccue23:12:10

so the error there is right

emccue23:12:24

to use recur, you need to have the call in the tail position

emccue23:12:01

not saying this logic is right

emccue23:12:59

(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))))))

Filip Strajnar23:12:53

so how can you use recur multiple times

Filip Strajnar23:12:02

is when literally the only construct with this issue?

Filip Strajnar23:12:10

well thats annoying

emccue23:12:44

^ i think this does what you want for a string

emccue23:12:52

okay so here is how you can think about recur

emccue23:12:02

lets say you wrote your function like this

emccue23:12:38

(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))

emccue23:12:21

you can see that all the calls there are “tail calls”

emccue23:12:34

so “recur” is a hint you can give to the compiler that you don’t need to maintain the whole stack

emccue23:12:21

(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))

emccue23:12:41

which is what lets you find the max depth of a fairly large string

emccue23:12:52

since you can’t have a callstack 10000 deep

emccue23:12:23

you can still do “non-tail” recursion, it just will have a callstack like all normal code

emccue23:12:52

loop is just a helper for doing a tail recursive procedure inline

Filip Strajnar23:12:04

why cant i just do normal jumps

Filip Strajnar23:12:07

normal for loop

dorab23:12:56

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.

emccue00:12:36

> why cant i just do normal jumps Because the language does not support that, for better or worse

emccue00:12:48

you can certainly make a macro to extend the language to have that support

emccue00:12:17

but most problems you solve with clojure wont be advent of code and you wont feel that absence as much

noisesmith19:12:37

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

noisesmith20:12:34

(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))

noisesmith20:12:17

the other solutions need to replicate the "examine input one token at a time" logic that reduce has built in

noisesmith20:12:42

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)

noisesmith20:12:28

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)

👍 1
Mike22:12:47

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?

Filip Strajnar22:12:47

I'd assume ps script is easier, if you build your own clojure jar, you have to setup a new PATH and all manually

seancorfield22:12:58

@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.

Mike22:12:31

okay cool. just wanted to make sure they're the same.

seancorfield22:12:46

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).

Mike22:12:05

@seancorfield thank you. i didn't see that channel before.

Mike22:12:03

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?

seancorfield22:12:44

You don't need to worry about the JAR. The CLI (`clojure` and clj) are shell scripts (bash on Linux/macOS, PS on Windows).

seancorfield22:12:07

Think of the JAR files as "just libraries" that your program will depend on.

seancorfield22:12:20

(no one ever needs to build Clojure themselves, unless they're actually going to be a contributor to it, rather than a user)

seancorfield22:12:59

(I actually use brew on WSL2 so I can easily install any version and upgrade/downgrade easily, just like the macOS experience)

Mike22:12:23

hm, thats not a bad idea. i have msys2 so i may do it that way.

seancorfield22:12:58

No idea how far you'll get with msys2. Unless it's improved a lot since the last time I tried it.

Mike22:12:28

the only caveat is for my use case, i need to be able to compile class files that are loaded in another program.

seancorfield22:12:02

You mean, reuse Clojure code in a non-Clojure program?

Mike22:12:09

but i guess that won't matter. i'll give it a try though.

Mike22:12:17

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.

Mike22:12:45

so i need to be able to "compile" as a java class

seancorfield22:12:18

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.

seancorfield22:12:24

That how we integrated Clojure into our legacy apps, a decade ago, and continue to work with that today.

seancorfield22:12:21

@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.

seancorfield22:12:57

We sometimes apply patches to our legacy production apps this way, via a REPL, from our editor, while it is running -- with zero downtime.

Mike22:12:48

awesome, thank you!

seancorfield22:12:11

The AOT compilation process is fraught with pitfalls, especially if you're trying to use it with "library" code to be honest.

Mike22:12:36

and i'll get that clojure.java.api with the clj ps script?

seancorfield22:12:26

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.

seancorfield22:12:40

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.

Mike22:12:59

@seancorfield thank you very much for your help!