This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-05-20
Channels
- # aws (7)
- # bangalore-clj (2)
- # beginners (64)
- # boot (34)
- # cider (1)
- # cljs-dev (8)
- # cljsrn (22)
- # clojure (268)
- # clojure-greece (2)
- # clojure-italy (8)
- # clojure-quebec (1)
- # clojure-russia (5)
- # clojure-spec (7)
- # clojurescript (7)
- # consulting (1)
- # cursive (184)
- # data-science (1)
- # datascript (18)
- # datomic (54)
- # dirac (1)
- # emacs (17)
- # graphql (1)
- # klipse (2)
- # leiningen (1)
- # off-topic (17)
- # onyx (10)
- # pedestal (2)
- # reagent (16)
- # spacemacs (4)
- # untangled (3)
- # vim (28)
- # yada (3)
a records (`defrecord`) question:
why does the map->
constructor actually silently agree to wrong key names and to invalid arities for the record type's definition, whereas ordinary record instantiation enforces the correct arity?
also ― how do you edit a multi-line function on the repl? when I paste a long function to the repl, it seems (by pressing the up-arrow) I can only bring up each of its lines on its own ― I'm probably doing it all wrong in terms of repl'ing 😉
On records, not completely sure I understand your question but records are not limited to just the well known keys - you can assoc any key into a record
The positional constructor doesn't have a way to do that but that's supported in the map constructor
On the repl, it will depend on the repl, but in many you can't. I typically use the repl indirectly by evaluating code from inside a source file where I have full editing capability for stuff like that.
if you have a lein repr open on a terminal do you need to open a second terminal to use system commands?
quick answer is yes, just have another tab/window open. Longer answer is learn about jobs in unix so you can "minimize" the repl and work in other jobs and return to the repl when you're ready (http://linuxcommand.org/lts0080.php) and then perhaps one more suggestion is make sure that you have a code environment setup that integrates text editing and repl interaction. For example CIDER, cursive, light table, vs code with plugins, etc
vitruvia: TL;DR control-z
to suspend the repl, fg
to resume. I'd stick to that minimal usage as fiddling many more jobs on the same window might lead to shooting yourself in the foot.
by the way thanks for this link on job control, I just read it and it is exactly what I was looking for
@alexmiller thanks, that makes sense about the repl.
@alexmiller about records, I am not too sure why it was considered useful to allow associating keys outside a corresponding defrecord
's set of keys.
I guess the clojure designers clan always prefer flexibility & extensibility over "safety", although this is fairly inconsistent here that one constructor is safe and the other is flexible; The inconsistency is further that the simple constructor requires all keys be supplied, whereas the map->
one allows omitted keys defaulting to nil
.
I might just be looking for symmetry/consistency in the wrong place: Symmetry/consistency is always cognitively helpful, but it can only be limited to certain subsets of the concepts of a system.
@matan if you want a specific set of fields what you want is exactly deftype
defrecord is if you want default fields, plus everything a hashmap does
@matan I think it's unreasonable to expect consistency between these two constructors - they are different because they give different affordances. Those differences are the benefits of the two approaches.
@matan: fwiw i have found it useful to read https://clojure.org/reference/datatypes repeatedly. my reading is that it's all about striking a balance between abstraction and performance in the host env.. defrecord supports assocking strange keys because that's what the abstraction calls for (they could have called it "defmap").
no - but the macro-expansion of a nested go-loop inside go could easily grow too large
and that's easy to fix by making a function that starts the go-loop
do you mean for readability purposes? or can a macro-expansion that's too large cause issues?
a macro that expands too large can create code that's too big to fit in a method, which means the code won't run
how does closing the channel stop the go-loop ?
by not calling recur
if you read from a channel, you can detect that it is closed if it returns nil, and not recur
it really depends on what you are doing in the loop though
a common pattern for a loop you want to be able to shut down, is to pass it a poison channel, and have it stop if the channel closes
you can use alts! to read from your normal channel, or the poison channel (whichever has data) and if you read from the poison channel, you take that as a signal to stop
alts! will let you know which channel you read from
so right now my code is structured kind of like:
(defn create-stream [handler]
(go (let [messages (<! (create-ws-connection "url"))]
(go-loop [] (when-let [{:keys message} (<! messages)] (handler message)))))
you know that could easily be a loop inside a go...
also, that go-loop doesn't loop
it reads one message, runs a handler, and exits
anyway, if you create a poison channel (just a normal channel that you close when you want the loop to exit) you can just turn the <! into an alts! and leave the rest the same
since it's already inside go
so is this the rough idea?
(defn create-stream [handler]
(go (let [messages (<! (create-ws-connection "url"))
poison (chan)]
(go-loop [] (let [[{:keys message} ch] (alts! messages poison)]
(if-not (= ch poison)
(do (handler message)
(recur)))))))
right, but you can literally change the go-loop to loop
and the args to alts! go in a vector
you don't need to check for poison
if the result is nil that means either your ws connection is closed, or poison is closed
both mean stop
so you can just check if what you read was nil
(defn create-stream [handler]
(let [poison (chan)]
(go (let [messages (<! (create-ws-connection "url"))]
(loop [] (let [[val] (alts! [messages poison])]
(if-not (nil? val)
(do (handler message)
(recur)))))))
poison)
(defn create-stream
[handler]
(let [poison (chan)]
(go (let [messages (<! (create-ws-connection "url"))]
(loop []
(let [[{:keys [message] :as m}] (alts! [messages poison])]
(when m
(handler message)
(recur))))))
poison)
so yeah, we ended up with pretty much the same thing, so looks good