beginners

David Peled 2025-09-26T14:54:37.996659Z

Hello, I'm David. New to Clojure, not to programming. Tryed implementing a function that will generate me as many multiples of n as I'd like.

(defn multiples-of
  [n]
  (iterate #(+ n %) n))
"Works fine" in the REPL:
(take 4 (multiples-of 2))
=> (2 4 6 8)
Unless I simply paste this:
(multiples-of 2)
When I do this it seems I have caused the repl to enter some kind of frozen state, probably computing "infinite" lists of multiples of 2, which made me confused: Doesn't the invocation of multiples-of should return some kind of function? Why is my REPL stuck? Looking to learn as-much-as-possible! Thanks!

David Peled 2025-09-27T07:33:11.931909Z

Brilliant. Thanks to all!

hrtmt brng 2025-09-27T09:09:56.166959Z

Lazy list means, that you can treat it like a normal list. You can copy it, or look at individual elements. But internally it is not a normal list. It is a special data structure, that is determined only on demand. You can ask for the first 1000 elements, but not for the last element if the list is infinite. And you cannot print an infinite list. The existence of such a concept is very special in Clojure and a few other languages. But it is very often exactly what you need and then it is very nice that you don’t have to implement this by hand. For example if you need the list 1,2,3,4,…, you can use (range). You don’t have to say until what value you need this list. This means one possible bug less. You simply take as many values as you want and you can be sure, that you don’t waste space or computation time for the values, you don’t need.

David Peled 2025-09-27T10:08:56.785929Z

Yup, I agree with the sentiment @hbrng.computer, exactly the reason why I decided to pick up clojure. In this instance, I was quite surprised that the REPL provided by my Cursive IDE allowed itself to get hung up like that by default because of an infinite lazy-sequence, which made me wonder about the possible dangers of infinite lazy sequences in my code-base - I like programming mistakes to fail fast and clearly, but here I could end up with a bug that could cause my program to hang up in production. Ofcourse, this is not a clojure problem per-say, as one could do a while true in any language. I was just surprised by this behaviour at first.

2025-09-28T16:36:10.769769Z

lazy data structures are a feature that differentiates clojure from most other languages, but they are IMHO more useful inside a function definition, and tend to cause problems when used as return values. eg. I had a huge problem in a production app that turned out to be caused by a tag-soup library returning a lazy-seq of tags. on the surface that seems very nice and idiomatic for clojure. the problem is that it created a thread for tag parsing, and didn't clean up the thread until you hit the end of the seq. we were consuming only until we found the data we needed, and crashing from thread exhaustion.

bg 2025-09-26T14:59:03.441399Z

@davidp.info1 Always a good idea to do this in the REPL:

(set! *print-length* 10)
(set! *print-level* 2)

bg 2025-09-26T15:01:10.420169Z

iterate returns an infinite lazy sequence that the repl will try to realize fully and will freeze the repl. Setting the above vars in the repl will provide guidance to the print functions.

David Peled 2025-09-26T15:11:39.618619Z

Thanks @bg, worked wonderfully. I was just about to ask how can I guard against this in the future - which is exactly what you provided. If I may, do I need to manually set these vars every time I start a REPL? Any canonical way to have a default configuration?

2025-09-26T15:18:23.813909Z

> do I need to manually set these vars every time I start a REPL? Any canonical way to have a default configuration? most editors/IDEs do this for you automatically when starting a repl

bg 2025-09-26T15:18:25.120339Z

@davidp.info1 You can persist the above and other things that you want to loaded in the repl by default by using the user.clj route. You need this file in your project/class path. More info here - https://clojure.org/reference/repl_and_main#_loading_of_user_clj Example user.clj:

(ns user)

;; REPL settings
(set! *print-length* 10)
(set! *print-level* 2)

❤️ 1
2025-09-26T15:20:15.052719Z

> most editors/IDEs do this for you automatically when starting a repl they will not set it globally, but eval every expression binding those to some value

👍 1
bg 2025-09-26T15:20:37.960889Z

@davidp.info1 Here's a succinct tutorial about using the repl effectively - https://clojure.org/guides/repl/introduction If you use an editor like Emacs/VSCode etc, the editor integration will provide its own repl defaults etc.

👀 1
2025-09-26T16:09:03.397529Z

And if it wasn't clear. The function returns a LazySeq, which is a form of object that encapsulates a list of the elements computed till now, and the function to compute the next element. But by default a LazySeq print itself by trying to compute all remaining elements and then printing them all. That print behavior is configurable as others have said.

👍 1
Colton Goates 2025-09-26T02:46:13.321539Z

I'm trying to learn test.check, but when I attempt to run (stest/check `my-function) it can't find the generators class on the classpath. My deps.edn looks like this: {:aliases {:test {:extra-paths ["test"]} :dev {:extra-deps {org.clojure/test.check {:mvn/version "1.1.1"}}}}} Does anyone know what might be the problem?

dpsutton 2025-09-26T03:21:48.283439Z

how are you starting the process?

dpsutton 2025-09-26T03:22:14.217099Z

do you have both the test and dev aliases on the classpath?

Colton Goates 2025-09-26T03:27:16.382499Z

I'm using CIDER to start a REPL. I don't know if I have the deps on the classpath, I thought the CLI tool was supposed to do that for me

dpsutton 2025-09-26T03:27:40.863629Z

they are in aliases. so it’s up to you to tell it to use those aliases

Colton Goates 2025-09-26T03:28:16.231229Z

Oh, okay. I think I remember the commands to activate aliases. Let me try that

dpsutton 2025-09-26T03:28:51.269979Z

you can do c-u m-x cider-jack-in and edit the startup command in the minibuffer and add :dev:test to the aliases

dpsutton 2025-09-26T03:29:38.976709Z

or use cider-clojure-cli-aliases defvar in emacs: https://docs.cider.mx/cider/basics/up_and_running.html#clojure-cli-options

Colton Goates 2025-09-26T03:46:22.013249Z

Like this?

(setq cider-clojure-cli-aliases ":dev")

Colton Goates 2025-09-26T03:46:51.149279Z

in dir-locals.el

dpsutton 2025-09-26T03:47:07.512569Z

yes. but that would get you the generators but not put your test alias on the classpath

Colton Goates 2025-09-26T03:48:40.780319Z

Okay, I would just add "devtest" to get them both, right?

dpsutton 2025-09-26T03:49:52.184759Z

exactly

Colton Goates 2025-09-26T03:50:02.379099Z

Thanks! When I try executing this elisp, I get: dir-locals--get-sort-score: Wrong type argument: listp, cider-clojure-cli-aliases

Colton Goates 2025-09-26T03:50:22.423559Z

Do you know why that might be happening?

Colton Goates 2025-09-26T03:50:28.730979Z

Let me get a full stack trace

dpsutton 2025-09-26T03:51:11.369709Z

here’s ours at work. does it look like this? It’s very easy to get dir locals wrong. they are dotted pairs and also the tree can be a bit confusing

Colton Goates 2025-09-26T04:00:21.103229Z

Oh, I didn't realize that dir-locals was a cons tree. I was thinking it was a normal elisp file. I ran the setq command and it worked, I'll just have to figure out the dir-locals thing--I'll let you know if I run into any other problems. Thanks for your help!

dpsutton 2025-09-26T04:00:35.290719Z

yes. it’s data, not code

dpsutton 2025-09-26T04:00:46.755159Z

yeah it gets confusing. happy hacking!