Fork me on GitHub
#beginners
<
2018-09-25
>
tagore01:09:37

@veix.q5 What would you be trying to convey by doing so?

quadron07:09:41

@tagore since clojure allows nil to propagate through the system, it seems weird that it does not propagate through a channel

quadron07:09:32

but what is nil if it's not a value?

quadron07:09:43

(count #{nil}) returns 1

orestis07:09:36

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.

orestis07:09:16

I think the common pattern there is to put some special sentinel value, usually a namespaced keyword to indicate special cases.

henrik08:09:23

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.

henrik08:09:04

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.

leonoel08:09:20

which race conditions require a sentinel value to be nil instead of anything else ?

henrik08:09:07

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.

leonoel08:09:56

how does this relates to nil ?

henrik08:09:28

Having nil mean “closed” means that stuff like

(go-loop [msg (<! ch)]
  (when msg 
    (println msg)
    (recur (<! ch))))
behaves as expected.

henrik08:09:39

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.

leonoel08:09:55

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.

henrik08:09:52

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.

stardiviner09:09:16

How can I combine (html/attr-values :href) and (html/attr-values :title) two functions just like with juxt ?

fmn09:09:56

Sorry to ask this here, are there any channel for general question about programming?

borkdude09:09:12

@funyako.funyao156 #off-topic is a very active channel where you can ask whatever you want

fmn10:09:45

@borkdude alright thank you!

Sy Borg10:09:11

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?

borkdude11:09:35

@sy_borg maybe try #cursive (as well)?

Sy Borg11:09:20

ok, I will

deliciousowl13:09:39

try writing ) and pressing enter

rymndhng16:09:20

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)}

andy.fingerhut16:09:19

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})

bronsa16:09:59

@rymndhng they can execute out of order

bronsa16:09:45

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}

seancorfield16:09:54

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!

bronsa16:09:10

the map needs to be read first :)

bronsa16:09:25

that's where the ordering gets "altered"

seancorfield16:09:35

Oh, of course! Doh!

seancorfield16:09:56

Code-is-data 🙂 That's a great example of that!

rymndhng16:09:38

@andy.fingerhut I was thinking of doing that precisely for that reason 😆 . @bronsa thanks for the explanation -- the reading part makes sense

bronsa16:09:29

@rymndhng alternatively (hash-map 1 .. 2 .. 3 ..)

👏 8
bronsa16:09:59

will guarantee evaluation order as that's just a normal function call, keys will still be unordered afterwards

rymndhng17:09:52

@bronsa good suggestion I think I'll do that instead because it's one less name to come up with!

manandearth19:09:20

shouldn't it eval to the same number?

hiredman19:09:04

user=> (= (java.lang.Math/pow 10  13) 10e13)
false
user=> 

hiredman19:09:37

user=> (* 1.08321 1e13)
1.08321E13
user=> 

manandearth20:09:19

understood.. how would you then type the volume of the Earth according to wikipedia 1.08321×10¹² km³ ? (https://en.wikipedia.org/wiki/Earth)

hiredman20:09:49

an number in scientific notation looks like mEn where the interpretation is m * 10^n

hiredman20:09:37

so in 1.08321×10¹²; m = 1.08321, n = 12; which gives you 1.08321E12

hiredman20:09:12

sorry exponential notation

hiredman20:09:56

whichever I guess

👍 4
Chase20:09:14

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?

jaihindhreddy-duplicate22:09:58

Rich also talks about it nil false and '() here: https://youtu.be/P76Vbsk_3J0?t=28m12s

hiredman20:09:42

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

hiredman20:09:01

someone wrote a blog post complaining about lazy seqs not being lazy enough

hiredman20:09:31

then rich added empty seqs, so now empty seqs are a thing distinct from nil

hiredman20:09:14

'() is both an empty seq and an empty list

andy.fingerhut20:09:59

Wasn't (empty? '()) true and (= nil '()) false even in the before time, though?

hiredman20:09:55

yes, but () was only the empty list (no need to quote it, the reader can tell it is the empty list)

johnj20:09:44

no just empty lists, (identical? some-list (seq some-list)) is also true

hiredman20:09:09

seq by definition returns nil on an empty seq (which an empty list is)

andy.fingerhut20:09:24

@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.

andy.fingerhut20:09:38

I'll see if I can find a quote in a transcript

hiredman20:09:50

so identical? on the empty list and the seq of the empty list is not true

hiredman20:09:32

user=> (identical? () ())
true
user=> (identical? () (seq ()))
false
user=> 

andy.fingerhut20:09:46

(= {} #{}) is also false in Clojure -- there are no sets that are equal to any maps, not even empty ones.

johnj20:09:04

Ok, was talking more about:

ghadi20:09:07

identical? uses reference equality and is almost never what you want when working with Clojure data structures

enforser21:09:08

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

hiredman21:09:50

clojurescript does a lot more at compile time

hiredman21:09:50

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

andy.fingerhut21:09:21

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.

andy.fingerhut21:09:44

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?

mfikes21:09:20

Things can even change with optimizations in the std lib. For example, this is changing:

(let [coll [1 2 3]] 
  (identical? coll (vec coll)))

mfikes21:09:33

I think the str / identical? behavior in ClojureScript is actually due to JavaScript.

hiredman22:09:00

but str also is a macro in cljs

mfikes22:09:04

Right, but in this case, this is true

(identical? "ab" 
  (apply str ["a" "b"]))

mfikes22:09:02

¯\(ツ)

Braden Shepherdson22:09:17

'ab' === 'a' + 'b' in Javascript; === is not as "identity" as one would hope 😞

Braden Shepherdson22:09:06

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.

mfikes22:09:49

Yeah, they are