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!Brilliant. Thanks to all!
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.
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.
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.
@davidp.info1 Always a good idea to do this in the REPL:
(set! *print-length* 10)
(set! *print-level* 2)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.
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?
> 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
@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)
> 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
@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.
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.
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?
how are you starting the process?
do you have both the test and dev aliases on the classpath?
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
they are in aliases. so it’s up to you to tell it to use those aliases
Oh, okay. I think I remember the commands to activate aliases. Let me try that
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
or use cider-clojure-cli-aliases defvar in emacs: https://docs.cider.mx/cider/basics/up_and_running.html#clojure-cli-options
Like this?
(setq cider-clojure-cli-aliases ":dev")in dir-locals.el
yes. but that would get you the generators but not put your test alias on the classpath
Okay, I would just add "devtest" to get them both, right?
exactly
Thanks! When I try executing this elisp, I get: dir-locals--get-sort-score: Wrong type argument: listp, cider-clojure-cli-aliases
Do you know why that might be happening?
Let me get a full stack trace
https://github.com/metabase/metabase/blob/master/.dir-locals.el
https://github.com/metabase/metabase/blob/master/.dir-locals.el#L79
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
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!
yes. it’s data, not code
yeah it gets confusing. happy hacking!