Fork me on GitHub
#beginners
<
2021-12-14
>
noisesmith00:12:45

I'm sure that works, I try to avoid editor / tool specific suggestions

Muhammad Hamza Chippa09:12:22

I have a vector like this [5 7 "missing" 10 "missing" 11] and I have to calculate something like this [0 (7-5) "missing" (10-7) "missing" (11-10)] seems very complicated to me how can I do that ?

Ed11:12:11

I'm not totally sure that I understand what you're trying to get out of it, but you could start with something like

(->> [5 7 "missing" 10 "missing" 11]
       (partition-by string?)
       (partition-all 3 1))
where we use partition-by string? to group the ranges of numbers separately from the "missing" values and partition-all to get a window on the collection, so you can extract the high and low values for the range? ... Does that make sense?

nmkip12:12:35

Maybe something like this?

(->> coll
     (filter int?)
     (partition 2 1))

Muhammad Hamza Chippa09:12:28

it is like difference between consecutive values but we have missing values in between

quoll14:12:52

It seems like a strange requirement (e.g. you can’t just filter out the missing values?) but assuming that the requirements are sound then you need some means of processing each element while also being able to see across the remainder of the input vector. One way to do that is with map-indexed. By having the index, you can look across vector up to that offset for the last thing to subtract from. Another way is with reduce where you accumulate your result, and keep track of the last non-missing value you saw as you process the list. The first thing that came to mind for me was to use a simple loop. Work backwards through the vector, and each value is either itself (when missing) or itself minus the first non-missing value in the remaining list.

(defn missing? [x] (= "missing" x))
(defn subtract
 "return 0 if the second number is nil"
 [a b] (if b (- a b) 0))

(loop [[n & r] (reverse v) result ()]
  (if-not n
    result
    (recur r (cons
               (if (missing? n)
                 n
                 (subtract n (first (remove missing? r))))
               result))))

quoll14:12:00

The reduce approach is to accumulate the result with the last non-missing value. Then use first to just get the result out (and drop the last valid value)

(first
  (reduce (fn [[result lastn] n]
            (if (missing? n)
              [(conj result n) lastn]
              [(conj result (subtract n lastn)) n]))
          [[] nil] v))

quoll14:12:54

Doing it with map-indexed doesn’t feel as “right” to me, but:

(map-indexed (fn [i n] (if (missing? n) n (subtract n (last (remove missing? (subvec v 0 i)))))) v)

quoll14:12:12

Obviously, there are more ways to do it 🙂 These are just the first 3 that came to mind. But the general principle is that whatever is processing the sequence needs to have some kind of context of where it is, so it can either look for the last non-missing value, or else remember what that last non-missing value was

Benjamin15:12:20

I'm thinking about having such aliases in my config

{:aliases {"foo" #{"888832288459194439" "888832288459194443"}}
 :m
 {:a #{"foo"}}}

 "expand"
=>

{:aliases {"foo" #{"888832288459194439" "888832288459194443"}}
 :m
 {:a #{"888832288459194439" "888832288459194443"}}}
1. do you have a better idea

zxspectrr15:12:44

hi, i've been working through advent of code and I find myself using recursion for a lot of the exercises. One thing I'm struggling with is falling into infinite loops. I was wondering if there are any easy tricks or patterns that I can use to avoid this? eventually the repl will break out of the loop but it can be a bit of a pain

Ben Sless15:12:32

If you can translate the problem to linear recursion (with recur) you can use reduce, then you know it will always end If not, could you perhaps model your problem as a postwalk of some data structure?

Ben Sless15:12:51

And always start from a stop condition

☝️ 2
zxspectrr15:12:09

i try and use reduce over loop / recur where I can

alexmiller15:12:27

inside a loop/recur, always first write an if with your stop condition

☝️ 2
alexmiller15:12:06

you will only fail to stop if a) you don't have a termination condition and b) you are not changing something in every pass through the loop that will eventually affect the termination condition

zxspectrr15:12:19

I think the latter is what hits me

Ben Sless15:12:27

Though I can say from personal experience that if you get it wrong then you'll still recur into infinity 🙂

zxspectrr15:12:28

eg i wait for a list to be empty. but it never is so round and round i go

alexmiller15:12:04

but those are the only two reasons why so explicitly think about both of them, both when you write and when you debug

zxspectrr15:12:06

is it generally more idiomatic to lean on reduce where the recursion is linear? at least then i know i'll be stopping

alexmiller15:12:28

in general, seq/transduce >> reduce >> loop/recur

zxspectrr15:12:37

ok that makes sense

Ben Sless15:12:46

Always have one binding which accumulates and one which decreases in size. Make sure you always check the one which decreases and that you always decrease it. It can be consuming a list or counting down to zero

alexmiller15:12:48

stuff on the left is higher-level and often better

zxspectrr15:12:53

it's probably a lack of practise also, i'm still getting comfortable with repl based dev

zxspectrr15:12:50

that's interesting, ben. Is the idea of the two bindings that if they're both the same then I can break out of the loop?

Cora (she/her)15:12:02

maybe also helpful is to be aggressive about extracting logic out to functions you can test independently so that the logic of the recursion ends up as simple as is reasonable

zxspectrr15:12:21

yep I try to do that also, but still quite new

Ben Sless15:12:10

That principle is explained in detail in the first chapter of sicp

Ben Sless15:12:38

Structure and Interpretation of Computer Programs

zxspectrr15:12:46

oh ok I've not heard of that

Ben Sless15:12:54

It's THE book

Ben Sless15:12:18

Highly recommended, even only the first few chapters

zxspectrr15:12:33

but I think something you've just said resonates wiht me. I have a recursive loop where I pass in a list and then filter inside the loop. I think if i filtered then recurred, I'd be able to diff the inbound list and break out of the loop:

(defn process-flashes [grid]
  (loop [grid grid
         flashed #{}]
    (let [points (keys grid)
          can-flash (remove flashed points)
          flashing (find-flashing grid can-flash)
          updated-grid (process-flash-points grid flashing)]
      (if (empty? flashing)
        updated-grid
        (recur updated-grid (into flashed flashing))))))

zxspectrr15:12:02

if points came in as an input param rather than in the let I guess I could see if it's decreased and break out if ithasn't

Ben Sless15:12:58

If you're filtering, why not use filter?

mknoszlig15:12:07

another possibility is to extract the recursion and termination condition by using iterate and focussing on the repeated application. this way you can take or drop as many results as you like to debug step by step

1
zxspectrr15:12:58

if i was using take while i could still end up in a loop though

zxspectrr15:12:10

but atleast I could control each take i suppose

Ben Sless15:12:15

Usually, your stop condition is when you have consumed all of your input and built all of the result, or if you have some early exit clause

zxspectrr15:12:42

yea i think if i moved points to an input param that could be my termination clause

zxspectrr15:12:55

then i could filter it on the way out rather than the way in

Cora (she/her)15:12:55

oh this is a great case for iterate, good call @U02QXKSHP97

mknoszlig16:12:18

@U027433844C i don’t want to spoil day11 for you. i noticed i did solve it using iterate . if you’re interested in taking a look after you’ve debugged, feel free to ping me, i’ll send you a link

zxspectrr16:12:35

that woudl be good mk

zxspectrr16:12:47

i did solve it mostly using iterate, but I have one part that uses loop

mknoszlig16:12:01

@corasaurus-hex thanks, i keep rediscovering it around AdventOfCode time every year 🙂

zxspectrr16:12:14

quite a lot of good input in this thread. To be honest I didn't expect so much feedback and help

zxspectrr16:12:17

very much appreciated

zxspectrr15:12:27

i thought about passing some sort of counter in as a paramter of the recursive call but this feels a bit clunky

dpsutton15:12:18

One rule of thumb is to construct the base case first. Then prove to yourself that you are going to work towards that base case in your recursion.

❤️ 2
Fra16:12:36

hi, I wrote the snake game in clojure using quil: https://github.com/francesco-losciale/snake is there anyone available to review these two code pages? how would you improve this code in terms of readability and conciseness? - note https://github.com/francesco-losciale/snake/blob/502c6794b174bfd15a8c358c2d59b8fd204f8c67/src/snake/grid.clj#L4 I had to use in grid.clj thanks

noisesmith17:12:34

in the future this is a good thing to post in #code-reviews

noisesmith17:12:46

the only real issue I see is the trailing ) on their own line - that hurts readability, we just don't do that

noisesmith17:12:31

top level def for constant values is normal, it would be an issue if you were mutating them mid run of the program, but you aren't

🙌 1
noisesmith18:12:10

I see that it's expected for quil to run your app when a namespace is required, in normal clojure this is considered pathological behavior

noisesmith18:12:57

the norm is that a namespace define a -main function if applicable, and then something outside it (eg. a repl invocation or an arg to clojure.main) arranges for it to be called

fappy20:12:15

hi 🙂 In  `try … catch … finally`  …. the contents of the finally clause has nothing whatsoever to do with the return value. It is intended for side-effect code only. After executing the code inside your finally clause, Clojure goes back to the last position reached in your try or your catch and returns that. This is a little surprising. It’s almost like “penultimately” instead of “finally”

dpsutton20:12:10

(try (println "in body") (finally (println "in final"))) i get “in body” before “in final”

fappy03:12:31

this returns the nil from the “in body” println instead of the nil from the “in final” println …. not that it matters in this case since they’re both nil

alexmiller20:12:25

comment was about the return value, not order of execution

alexmiller20:12:11

this is one place where Clojure's implementation differs from Java - the finally in Java can return and that takes precedence over the body

fappy20:12:48

given the existence of with- forms for closing off resources, what is the rationale for finally to be different from Java?

hiredman20:12:21

Expression vs statement languages

alexmiller20:12:24

I don't actually have a good answer for that but I suspect it has something to do with tail recursion

hiredman20:12:51

The with- forms don't just exist, they are macros implemented on top of try/finally

noisesmith21:12:45

@fappy to me it sounds like you are blurring the distinction between values and processes. clojure doesn't return the value of the last form in a body because it was the last thing processed, it returns it because that's the value that do is defined to return (`let` / doseq / fn / catch / finally / when etc. each have an implicit do - notably for does not).

Sam Ritchie21:12:19

(defn try-finally [try-thunk finally-thunk]
  (let [ret (try-thunk)]
    (finally-thunk)
    ret))

Sam Ritchie21:12:28

“finally” is a potentially confusing name 🙂 HUGE plug btw for the naming chapter in “Elements of Clojure” by @ztellman: https://leanpub.com/elementsofclojure/read_sample

Filip Strajnar23:12:02

does clojure happen to have type hints?

Sam Ritchie23:12:38

^TypeName before a symbol, yep

dpsutton23:12:54

This question makes me nervous. Clojure has a concept called type hints but it might not be similar to what you have in mind.

👍 1
hiredman23:12:10

there are also kind of two distinct ways type hints in clojure are used

Filip Strajnar23:12:45

oh, https://clojuredocs.org/clojure.reflect/typename this is absolutely not what i thought it would be 🙂

noisesmith23:12:18

wait what does typename have to do with this?

hiredman23:12:24

the main one is they direct the compiler on how to compile interop forms

dpsutton23:12:28

your intuition is correct, because that’s not a typehint

Filip Strajnar23:12:41

if anyone happens to know how python has type hints, and where IDE nicely tells you this is supposed to be a string when you try to assign int?

Filip Strajnar23:12:53

that's what i imagined in my head 😅

noisesmith23:12:03

so you mean an IDE feature, not a language feature

Filip Strajnar23:12:28

or even better, a language feature, like typescript compiler

noisesmith23:12:33

clojure doesn't have IDE features per se, you should probably ask in the channel for your IDE?

hiredman23:12:46

clojure type hints are hints you the programmer give the compiler

noisesmith23:12:51

what would the language feature do?

hiredman23:12:55

you are asking for hints the compiler gives you the programmer

hiredman23:12:29

definitely not built into the language

Filip Strajnar23:12:51

probably not exactly a feature most clojure developers would seek anyway?

hiredman23:12:31

there are lots of tools, cursive (the intellij plugin), https://typedclojure.org/ that might sort of provide that

hiredman23:12:48

but I don't actually use any of those

Cora (she/her)23:12:03

clj-kondo has some nice type hinting at places

hiredman23:12:12

I was just going to say

hiredman23:12:25

clj-kondo is a linter which is pretty popular

Cora (she/her)23:12:31

it's not like, say, java or typescript or anything, but it's helpful

Cora (she/her)23:12:39

add in clojure-lsp and you've got a stew cookin'

Cora (she/her)23:12:55

(I think clojure-lsp uses clj-kondo under the hood for things?)

borkdude23:12:58

correct, both for analysis and diagnostics

1
Filip Strajnar23:12:51

this is indeed the type of thing i was intending to ask about, although i probably articulated myself poorly

noisesmith23:12:34

thinking about why I don't look for this feature - my code is usually in one of these contexts: • the data type is sequential collection • the data type is associative collection • I'm at an edge / in the bottleneck working on a specific primitive data type • I'm doing interop with a well defined and documented class from java

hiredman23:12:42

by far any given name is mostly going to be bound to a map or a seq

noisesmith23:12:17

also I very rarely find myself using third-party-glue, which is where I'd most likely need these tips

noisesmith23:12:30

clojure.core is small enough that I can expect I know most of it

noisesmith23:12:17

like - I know what this weird data type is because I wouldn't even be using this library except for the need to use the data type

noisesmith23:12:26

(or I knew it already because this is core)

hiredman23:12:13

clojure.spec might be something to look, which is a way to describe schemas for clojure data, but it is pretty dynamic at runtime stuff

noisesmith23:12:30

@filip.strajnar to be clear I'm not trying to convince you you don't need the feature, just figuring out loud why I haven't missed it myself

hiredman23:12:32

not alot of static inspection and checking

hiredman23:12:02

(none built in, I think there have been some projects looking at it, but it is hard to add because of how open ended spec is)

Cora (she/her)23:12:36

I rather like types in typescript. it feels like a good middle ground between stricter type systems and completely dynamic systems

Filip Strajnar23:12:39

oh, don't worry i have exactly 0 experience with clojure, so i don't have any reason to assume my previous ways would be relevant at clojure

Filip Strajnar23:12:55

it'd be unfair of me to say i'm making a good suggestion

Filip Strajnar23:12:04

or rather, making a good question

dpsutton23:12:02

you were asking if a concept you are familiar with had an analogue here. that’s fair 🙂 . And I would also love a static type system with row polymorphism. Its not unfair to want that 🙂

Filip Strajnar23:12:04

for context, I'm a developer using C# (http://ASP.NET core), so I'm simply asking around for familiar concepts from this language

borkdude23:12:26

clj-kondo offers a basic form of type checking.

(+ 1 :foo)
     ^--- Expected: number, received: keyword.

noisesmith23:12:48

if there's a continuum, clojure is definitely pretty far to the "not specifiying types" extreme, I've found it normal to need to read definitions down two or three deep before I understand what data types are being used when looking at other people's code

noisesmith23:12:02

but there's a silver lining that very few types are used in practice

Filip Strajnar23:12:47

I was mostly attracted/curious about clojure because of macros

seancorfield23:12:56

Probably worth mentioning that macros are not very widely used in real-world code in general. We try to do as much as possible with regular functions and use macros to provide "syntactic sugar" over function calls. A few things require macros -- those involving deferring evaluation, for example, such as when (which is a macro that expands to an if expression).

seancorfield23:12:03

Macros don't compose like functions so they aren't a good "API" in general but they can be useful to provide a sort of DSL for humans (often implemented on top of a programmatic API that does compose).

phronmophobic00:12:07

While it's true that some programs and systems don't make much use of macros, they are 1) a critically important feature of clojure and lisps and in general 2) worth learning! Macros provide capabilities that aren't always needed, but really save the day when they are. Macros also provide a playground for learning concepts that are otherwise less accessible. I agree that it's a nightmare if you need to work on a code base that uses macros unnecessarily and gratuitously, but if you're interested in macros, I really do encourage you to try them out!

seancorfield00:12:23

Right, agreed, but when I see someone attracted to Clojure "because of macros", I always feel it's worth tempering that with "Hey, wait a minute there... that's not the real reason to use Clojure!" 🙂

phronmophobic00:12:56

When I see someone excited about learning macros, it reminds me of why I initially decided to learn clojure and how much fun it was. If that's what someone is excited about, I say "go for it!"

seancorfield00:12:48

Hahaha... okay, fair enough. This old curmudgeon has had a rough several days so I'm even more grumpy than usual! get off my lawn!

😁 1
quoll02:12:18

If I were to be attracted to a language because of macros, I’d go to Racket. Indeed, Racket is on my list of languages to learn specifically because of its famous macros

borkdude23:12:48

and it's easy to inspect in the REPL what flows through functions

Filip Strajnar23:12:10

macros are very foreign in C#, so it sounds exciting

noisesmith23:12:14

@borkdude that too - the observability and reflection make a big difference

Filip Strajnar23:12:56

as for recommended recourses, is there a book or some articles i should read on?

Filip Strajnar23:12:05

or should the clojure website tutirial suffice

Cora (she/her)23:12:26

I loved Getting Clojure, not sure how other people feel about it

💯 1
william09:12:56

I second this, the introductory book I liked the most!

borkdude23:12:28

Here is a list that might be useful: https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f You can also do exercises here: https://4clojure.oxal.org/

👍 1
💯 1
noisesmith23:12:28

outside the box a little: IMHO most of clojure's best ideas come from ml and scheme, and spending a weekend on each of those will leave you well prepared to write good clojure code

Filip Strajnar23:12:34

surprisingly large amount of resources to be honest

noisesmith23:12:42

(and both are excellent languages for learning about languages)

Filip Strajnar23:12:22

thank you all so much for all the information

Cora (she/her)23:12:30

there are so many resource on there

Filip Strajnar23:12:38

may I also ask about which IDE/Text editors you use?

Filip Strajnar23:12:17

@corasaurus-hex I see, the list borkdude sent was great too

Filip Strajnar23:12:32

i didn't expect this many resources for this language

seancorfield23:12:56

Probably worth mentioning that macros are not very widely used in real-world code in general. We try to do as much as possible with regular functions and use macros to provide "syntactic sugar" over function calls. A few things require macros -- those involving deferring evaluation, for example, such as when (which is a macro that expands to an if expression).

Cora (she/her)23:12:28

easiest to get started with, right now, is probably calva (if you don't already know emacs or intellij)

Cora (she/her)23:12:46

that's totally my opinion, of course, and others here may disagree

seancorfield23:12:46

If you already know VS Code, I'd agree with the suggestion to use Calva for Clojure @filip.strajnar

Filip Strajnar23:12:54

I know a bit of vim, no emacs

Filip Strajnar23:12:01

i've used intellij, but i dislike it

Filip Strajnar23:12:14

i'm very fond of VS code

borkdude23:12:25

Then #calva it is :)

seancorfield23:12:38

Join the #calva and #vscode channels to get questions answered!

Filip Strajnar23:12:56

thanks :thumbsup: