This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-09-25
Channels
- # 100-days-of-code (3)
- # beginners (80)
- # cider (53)
- # cljdoc (9)
- # cljs-dev (66)
- # cljsrn (6)
- # clojure (108)
- # clojure-austin (5)
- # clojure-dusseldorf (1)
- # clojure-italy (9)
- # clojure-losangeles (2)
- # clojure-nl (4)
- # clojure-spec (40)
- # clojure-uk (46)
- # clojurescript (60)
- # cursive (119)
- # datomic (28)
- # emacs (10)
- # events (1)
- # figwheel-main (21)
- # fulcro (16)
- # hyperfiddle (10)
- # immutant (2)
- # leiningen (1)
- # liberator (5)
- # nyc (1)
- # off-topic (36)
- # onyx (4)
- # pedestal (52)
- # portkey (5)
- # re-frame (11)
- # reagent (11)
- # shadow-cljs (46)
- # sql (1)
- # unrepl (2)
@tagore since clojure allows nil to propagate through the system, it seems weird that it does not propagate through a channel
AFAIK, in core.async specifically, nil has a special meaning — the channel is closed. This is why you aren’t allowed to put nil
into a channel.
I think the common pattern there is to put some special sentinel value, usually a namespaced keyword to indicate special cases.
Yeah, as above. Or if you’re modelling messaging, you can always create a map representing messages,
{:status :error
:data nil}
And that’s fine. Of course, this is equivalent to not populating the :data
part at all.Once you learn to work with it, nil
indicating a closed channel is actually pretty handy, and fits well with how Clojure functions are normally constructed. It’s also constructed that way to prevent a certain category of race conditions.
Since you can’t check if a channel is open (and you can’t peek at values) without taking, you also can’t end up in a situation where two flow control structures are waiting for each other.
Having nil
mean “closed” means that stuff like
(go-loop [msg (<! ch)]
(when msg
(println msg)
(recur (<! ch))))
behaves as expected.The alternative would be… designating a specific keyword to mean “closed”? And then that keyword would be disallowed, and the when
above would be more complicated to express. nil
makes more sense.
nil
is arguably a good default to represent EOF, but it's surprising that it's enforced in API, as every other datastructure allows holding nil
. For instance, <!
could have been designed like get
, where you can give your own sentinel if you want to treat nil
as an acceptable value.
On the other hand, unlike data structures, channels are completely opaque, and you interact with them by mutating them. get
doesn’t mutate, and the entire data structure it operates on is transparent to it.
How can I combine (html/attr-values :href)
and (html/attr-values :title)
two functions just like with juxt
?
@funyako.funyao156 #off-topic is a very active channel where you can ask whatever you want
hello.
I reinstalled IJ Idea and Leiningen from scratch and now I have an issue with REPL (everything worked fine before).
REPL itself runs fine (both out of IJ and in it), but when I try to send a piece of code to REPL (after switching NS) I get
CompilerException java.lang.RuntimeException: Unable to resolve symbol: defn in this context, compiling: [...]
What did I miss?
try writing ) and pressing enter
Question about map creation order using the literal syntax:
If I have a clojure map literal whose values come from side-effects, are the side-effects evaluated in order of pairs first?or can they execute out of order (wondering because iteration over map keys is not insert oriented)
Example:
`
{:foo (side-effect-1)
:bar (side-effect-2)
:baz (side-effect-3)}
I am not sure, and that leads me to suggest that you write it in a way where the order is obvious, if you care about the order, e.g. (let [a (side-effect-1) b (side-effect-2) c (side-effect-3)] {:foo a :bar b :baz c})
user=> {1 (println 1) 2 (println 2) 3 (println 3) 4 (println 4) 5 (println 5) 6 (println 6) 7 (println 7) 8 (println 8) 9 (println 9)}
7
1
4
6
3
2
9
5
8
{7 nil, 1 nil, 4 nil, 6 nil, 3 nil, 2 nil, 9 nil, 5 nil, 8 nil}
Thanks @bronsa -- I was about to ask for an example since I couldn't imagine why the compiled order of evaluation wouldn't be strictly L-to-R, even if the underlying map would be unordered!
Oh, of course! Doh!
Code-is-data 🙂 That's a great example of that!
@andy.fingerhut I was thinking of doing that precisely for that reason 😆 . @bronsa thanks for the explanation -- the reading part makes sense
will guarantee evaluation order as that's just a normal function call, keys will still be unordered afterwards
@bronsa good suggestion I think I'll do that instead because it's one less name to come up with!
shouldn't it eval to the same number?
understood.. how would you then type the volume of the Earth according to wikipedia 1.08321×10¹² km³ ? (https://en.wikipedia.org/wiki/Earth)
an number in scientific notation looks like mEn where the interpretation is m * 10^n
I'm curious about something. why does (empty? '())
evaluate to true but (= nil '())
is false? How would you explain nil
in terms of what I should be thinking about when learning programming/clojure? is that too broad a question?
Rich also talks about it nil
false
and '()
here:
https://youtu.be/P76Vbsk_3J0?t=28m12s
long ago in the before time there was no such thing as an empty seq in clojure, you just had nil. this actually limited the laziness of seqs because it meant that whenever you called rest on a seq, it had to atleast realize the next element in the seq in order to decide if it was returning a seq cons cell or nil
Wasn't (empty? '())
true and (= nil '())
false even in the before time, though?
yes, but ()
was only the empty list (no need to quote it, the reader can tell it is the empty list)
@chase-lambert I think the semi-philosophical answer from Rich is that nil is nothing, but an empty collection is not nothing, it is a collection of a particular type that contains no elements.
I'll see if I can find a quote in a transcript
it's explained here https://clojure.org/reference/lisps
Search for "nil means nothing" in Rich's talk "Clojure for Lispers" here: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/ClojureIntroForLispProgrammers.md
(= {} #{})
is also false in Clojure -- there are no sets that are equal to any maps, not even empty ones.
identical?
uses reference equality and is almost never what you want when working with Clojure data structures
I assume it's because of how js and java differ in storing stuff as references, but it makes me uneasy when core functions in clj and cljs don't return the same thing when given the same input
;; Clojure REPL
(identical? "a" (str "a")) => true
(identical? "ab" (str "a" "b")) => false
;; Clojurescript REPL
(identical? "a" (str "a")) => true
(identical? "ab" (str "a" "b")) => true
I would guess str
on cljs there is actually a macro that detects that you have literal strings and expands at macro expansion time to the same constant string, and then maybe the closure compiler hoists and unifies both constants
If they return different things for =
I would worry a lot more (and suspect there probably are some corner cases where Clojure/Java and ClojureScript differ there). identical?
is an implementation detail if you are dealing with immutable values.
If you are planning to write code that relies on the return value of identical?
you may want to ask more about it, since in many, many common cases I would expect you will drive yourself crazy relying on identical?
Things can even change with optimizations in the std lib. For example, this is changing:
(let [coll [1 2 3]]
(identical? coll (vec coll)))
'ab' === 'a' + 'b'
in Javascript; ===
is not as "identity" as one would hope 😞
I don't think it's possible to be consistent here, if CLJS strings are JS strings, as one would hope for interop and performance.