This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # babashka (11)
- # beginners (62)
- # calva (9)
- # cider (7)
- # clj-kondo (18)
- # cljfx (2)
- # cljsrn (4)
- # clojure (55)
- # clojure-australia (3)
- # clojure-dev (2)
- # clojure-europe (39)
- # clojure-italy (3)
- # clojure-nl (3)
- # clojure-spec (5)
- # clojure-uk (5)
- # clojured (7)
- # clojurescript (16)
- # clojureverse-ops (2)
- # conjure (22)
- # cursive (12)
- # data-science (13)
- # datomic (8)
- # duct (7)
- # emacs (11)
- # events (1)
- # fulcro (12)
- # helix (10)
- # integrant (21)
- # introduce-yourself (5)
- # jobs (2)
- # jobs-discuss (16)
- # lsp (1)
- # malli (5)
- # meander (7)
- # membrane (9)
- # pathom (9)
- # reitit (5)
- # releases (3)
- # ring (2)
- # sci (18)
- # shadow-cljs (35)
- # sql (15)
Hey everyone, I am trying spit the output of a
distinct call which returns a lazy sequence. Thing is, it writes
clojure.lang.LazySeq@4e9d9e in the text file instead of a list that I want. How do I force the lazy sequence to be computed so that I can write it into a file?
it depends on how you are outputting the sequence to the text file as you could use
doall or see the answer here which uses
pr-str on the sequence
fun fact: the entire lazy sequence is realized in order to calculate the hash for the sequence that's part of the string, it's just that the toString method for clojure.lang.LazySeq doesn't show the sequence contents doall is irrelevant here
(cmd)user=> (def s (distinct (map #(doto % println) [1 1 1 2 3 2 3 4 5 4 3 2]))) #'user/s (cmd)user=> (str s) 1 1 1 2 3 2 3 4 5 4 3 2 "clojure.lang.LazySeq@1c3e4a2" (cmd)user=> (doall s) (1 2 3 4 5) (cmd)user=> (str s) "clojure.lang.LazySeq@1c3e4a2" (cmd)user=> (pr-str s) "(1 2 3 4 5)"
what would make a call chain like this clean? (resembling
some-> with a custom predicate?)
(let [context (call1 context)] (if (continue? context) (let [context (call2 context)] (if (continue? context) (call3 context) context)) context))
That was my first thought too, but I don't think it'll work quite right if you want to keep calling
(continue? context) on the updated value of
context is an immutable value).
Oh that's right, the predicates are for the original input (I always assume they get threaded as well). I suppose you could repeat the actual code as part of the predicates but it wouldn't be pretty...
The new context is the result of the last computation. In the case below, the context after would be 2 and false? Would be the continue. (cond-> 1 ; we start with 1 true inc ; the condition is true so (inc 1) => 2 false (* 42) ; the condition is false so the operation is skipped (= 2 2) ( 3)) ; (= 2 2) is true so ( 2 3) => 6 ;;=> 6
So it would work right? But it looks like it also could just be a recursive call.
Personally, I would probably do something like
just because I know future me will be able to read that code and know exactly what’s going on.
(let [context (call1 context) context (if (continue? context) (call2 context) context) context (if (continue? context) (call3 context) context) ... ] context)
It’s a bit boiler-plate-y, but you avoid nesting x levels deep, and the repetition makes it obvious that it’s just repeated iterations of “call a fn and check for continue?”
Well the repeating part is the specific predicate and the call so (Loop [calls [a b c] context] (let [call (first calls)] (if (and call (C? Context)) (recure (rest calls) (call context) context) or something, lisp isn't easy to your type on a phone.
I would avoid reduce and recursion for something like this. I think I'd rather do it like @U06CM8C3V said or even with
(as-> (call1 context) context (if (continue? context) (call2 context) context) (if (continue? context) (call3 context) context) ...)
you meant something like this with reduce?
(reduce (fn [context f] (let [new-context (f context)] (if (continue? new-context) new-context (reduced new-context)))) context [call1 call2 call3])
this is what i wrote as an example:
(reduce (fn [n f] (if (int? n) (f n) (reduced n))) 0 [inc inc str inc]);; => "2"
It's harder to read, you have to put some effort to understand what's going on.
Smarter is not always better. 🙂
In that sense, my solution using
as-> is also less readable than just using a
When i look at the let binding solution i have to scan each word twice to see if things are reused, and whats really changing, so to me, that's harder to read.
@UEQPKG7HQ reduce is only harder to read if you aren't used to it, IMHO it's worth getting used to
it's simpler to me than deep nesting of conditionals and here it conveys two important pieces of information that ease the reading of the code: there's a series of inputs that are always handled the same way, and any one of them could be a short circuit condition
i didnt find reduce easier to read either, but thats why im asking in beginners.. it might make sense to actually def and name the paramterized reduce function - but what would be a nice name for that?
stop-when-not-int (or some more succinct version of that) could work
I find that the patterns of reduce functions are so predictable I rarely need names for clarity, it's always a function of two parameters, the first arg is always the init or return value of the last call, the second arg is always an item from the collection being traversed
those properties might seem arbitrary but they never change so you only need to figure them out once
this came to me multiple times, thats why i thought there might be an idiom for this (or a threading where
some-> is a special case like
(def some-> (partial something-> nil?))
but that doesn't account for the repetition of applying some function to a previous value, then halting if a condition is met
;; and reduce does capture that pattern easily (reduce (fn [state f] (let [next-state (f state)] (if (pred? next-state) next-state (reduced state)))) initial-state functions)
Why does this fail (hangs indefinitely in repl):
(<!! (go (>! (chan) (+ 41 1))))
(let [c (chan)] (<!! (go >! c (+ 41 1))))
(Update: nope, left off a paren!)
In other words, making a new channel inside a go block seems to be a no-no, the channel has to be made outside the go block even though the go block returns its channel...
Thanks for any answers, mostly just trying to wrap my head around this conceptually.
the corrected version hangs just like you initial example did:
user=> (let [c (chan)] (<!! (go (>! c (+ 41 1))))) ...
an unbuffered channel is a synchronous exchange point, a reader and writer need to meet
Thanks, ya I must be misunderstanding the async/go docs, this works fine:
(let [c (chan)] (go (>! c (+ 41 1))) (<!! c))
But I thought go returns its own channel you could wait on? Per the https://clojure.github.io/core.async/#clojure.core.async/go
I thought this meant I could feed that channel directly to <!!. But I'm clearly misunderstanding that line or how it fits into async.
Returns a channel which will receive the result of the body when completed
you are are writing to a channel in the go block, but nothing is reading from the channel, so the go block waits for a reader, so the go block doesn't complete
so the channel returned from the go block never has anything written to it and is never closed
yeah, core.async is often counterintuitive, it's just more intuitive than the alternatives in its best use cases
Hi team, in an if statement if condition is true I must return "return true" value if false I need to call a function (in an example (+ 4 5)) before returning "return false" string (value of "x" does not related to the value that must be returned when the statement is false) I found a way of implementing this but do not think this is a good approach because I do not use "x" here. What is the idiomatic way of implementing described functionality?
(if false "return true" (let [x (+ 4 5)] "return false"))