This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-06-03
Channels
- # beginners (112)
- # boot (13)
- # cider (17)
- # cljsjs (2)
- # cljsrn (8)
- # clojure (57)
- # clojure-spec (2)
- # clojure-uk (5)
- # clojurescript (51)
- # cursive (4)
- # data-science (15)
- # datomic (1)
- # duct (17)
- # garden (4)
- # lein-figwheel (49)
- # midje (1)
- # nyc (1)
- # off-topic (8)
- # pedestal (1)
- # portkey (20)
- # re-frame (4)
- # reagent (27)
- # ring (1)
- # shadow-cljs (24)
- # spacemacs (7)
- # specter (3)
- # sql (5)
- # yada (5)
Hi, I am dealing with some state in my library where I find myself doing (swap!)
on two or more atoms sequentially, as they represent a different kind of state, but sometimes I have operations that change more than one at same time. Don’t need multi-threading, just 1 thread can handle them. So I think, maybe all of this should be just 1 atom with nested maps. Is it fine / recommended to have atoms with big nested maps, over having smaller maps distributed through different atoms? Is there any “general” rule when facing this kind of situations? Thanks!
(Or refs for performance critical scenarios, which are fairly rare)
☝️ agreed. On the web end of things especially, there’s been an increasing trend toward holding all state in a single,“database-like” complex nested atom. My team & I have done that on a number of projects, including one that intrinsically has to represent a great deal of state, and have been happy about that approach.
On the server side, there's rarely any use case for atoms for web stuff. If you're using atoms for mocking database components in memory, prefer several atoms so that you don't rely on more consistency that you will get in production. If you need to make a mutable, non concurrent algorithm, use a volatile instead, which will be faster and make the "this thing is not shared" intent more obvious.
Hi, my use case is actually keeping the state of subscriptions of a websocket connection. I don’t manage the websocket server, though, only a client. The server offers it’s own semantics for subsribe to channels, and want to keep the subscriptions the user has, it’s state, and sometimes the data coming from the websocket changes certain state
Thing is, websocket can close, so subscriptions on the “websocket” are lost, but I want to offer reconnection / resubscribing of the channels the library user already subscribed. So the lib user does not have to handle reconnectios / etc. I also need to keep in state the current websocket connection used, ’cause when it closes, a new one has to be created and put into the state to send messages to that one
So I have 2 “sources” of events (core.async ch), the user and what comes from the ws, and I need to keep the state of the connection, user interactions, websocket subscriptions, …all in a consistent way. I am thinking that the composite atom solution seems the best / simplest
Note that of you are using core.async, state can also be kept in locals in a go loop, thus avoiding mutations. It depends on how you want to read
You’re right, it could be an option. I am still figuring out if I need to read from outside or not. Is it easy to test the state if it’s local to the go-loop? Or I wouldn’t test the go-loop itself, but all the changes in a (fn old-state) -> new-state manner independently? I also need to perform some actions depending on the new state when consuming a new message, but that can be solved either having “pending actions” in the state itself, or having 2 local variables
Hey all, I just started reading about Data Oriented Design. Would people say that this aligns to the goals, or thought processes, of Clojure? I have seen that people are encouraged to think of data while working with Clojure, but, while reading through Data Oriented Design articles, I can't immediately tell if these are the same things. Example talk https://www.youtube.com/watch?v=rX0ItVEVjHc
I’ve watched this video. Mike seems to be talking about the benefits of data-oriented design from a performance standpoint
i.e. reasoning about data first (instead of objects first) makes it easier to think about how to avoid cache misses and so on
Yes, it appears to usually be referenced when considering high performance game development. Does this thought process overlap with functional programming in any way?
I generally don’t think functional programming (or object-oriented programming for that matter) is a great option if you really need all the performance you can get
hmmm perhaps not all concepts, but, some can be borrowed. For example, pure functions. Not that pure functions are the only defining characteristic of a functional language, but I imagine there are high performance systems that benefits from lessons of functional programming. My goal, in the end, is to better understand the lessons of these different systems so they can be blended together in a sane way.
That said, I do like to program in a data-oriented approach. I find writing programs as data + data transformations jives really well with how I do things
> I imagine there are high performance systems that benefits from lessons of functional programming. That’s certainly true. Rust is a good modern example of that I think.
@UAH9ECH9V Data oriented design is a performance optimisation and should not be confused with data driven design
so maybe kind of a silly question, but I'm curious... what is the actual difference between using .toLowerCase
vs using clojure.string/lower-case
?
I figured that ... so in that case.. why use the latter at all rather than just use the Java class directly?
ah gotcha.. thanks! As minor as a thing like that seems to be, I'm just trying to figure out the reasoning behind decisions to use what and when.
Also, the Clojure one is portable to Clojurescript
@ryan.russell011 also you can treat lower-case
as a first class function, which is not possible with .toLowerCase
:
(map string/lower-case ["Alice" "Bob"])
.toLowerCase
requires a wrapper
(map #(.toLowerCase %) ["Alice" "Bob"])
Hi I'd like an under the radar clojure function that is very useful to you
juxt is underrated
and fnil
some->
oo could you tell me about juxt?
fnil i have found very useful for the case where i'm conj'ing into a #{set}
juxt takes N functions and returns a function calls each function on its args and puts them in a vector
user=> ((juxt - * / +) 42 3)
[39 126 14 45]
user=> ((juxt - * / + > <) 42 3 1)
[38 126 14 46 true false]
Awesome
easier to reason about when i think of it as applying the function to the params in columns
right, each function you give it generates a column, calling the resulting function gives you a row
that's very interesting. one could store a lot of information in juxt result vectors ... with meaningful juxt fns
I am confused by
yup that's a classic use for juxt
the first col is the result of take 3
and the second the result of drop 3
sneaky.
i would like to make a reference one day of clojure phrase synonyms
sometimes there are many ways to achieve the same effect
so juxt can be a million fns
i'm trying to stretch my imagination since sometimes i assume limits where there are none! xD
@sova here's a macro that's very useful when debugging
(defmacro locals
[]
(into {}
(map (juxt (comp keyword name)
identity))
(keys &env)))
(speaking of juxt)
okay, looks cool. spits out a list of local vars?
ledger.balance-loader.unit-tests=> (defn foo [x] (let [y (+ x x)] (println (locals)) y))
#'ledger.balance-loader.unit-tests/foo
ledger.balance-loader.unit-tests=> (foo 2)
{:x 2, :y 4}
4
Really cool - thank you very much for this! Here's also an interesting implementation of a similar function: https://www.safaribooksonline.com/library/view/clojure-programming/9781449310387/ch05s08.html
magic!
wow cool
very useful for debugging for obvious reasons
one of the rare macros that has no use or need for quoting as well
noisesmith, you just made my day
thank you again kind fellow, that is so sweet
can you explain to me why i only need to call (locals) within a fn and not pass it anything? like... i get that they share scope but i don't understand how exactly
scope is nested - you can see surrounding context from a macro, through the implicit &env binding
(it's actually an invisible arg passed into the macro by the compiler)
there's also a &form binding which gives you the raw form which the macro saw before running
wow, cool! &env and &form
i feel like i'm looking inside clojure's brain
are there any others than &env and &form?
thank you @victor.cleja
ledger.balance-loader.unit-tests=> (defmacro wizardry! [& whatever] (prn ((juxt type (partial map type) identity) &form)))
#'ledger.balance-loader.unit-tests/wizardry!
ledger.balance-loader.unit-tests=> (wizardry! 1 2 3)
[clojure.lang.PersistentList (clojure.lang.Symbol java.lang.Long java.lang.Long java.lang.Long) (wizardry! 1 2 3)]
nil
it's fun trivia, maybe not apropriate #beginners material
damn so cool. the symbols
no it's perfect, i'm trying to expand my knowledge on the whole #!
so clojure.lang.Symbol and stuff, the compiler uses these to arrange everything properly and through some magic makes computations happen?
notice I used juxt again there
I asked it for the type of &form - list
then (partial map type) - you know what that would return, right?
so what that's showing is that &form contains a list of the values in the macro call before anything is done to them, including the macro symbol itself
quick question, is it correct to say (partial map form) is a transducer?
oh i see. juxt and partial play together nicely
super sweet!
no, (partial map f) is a function, that takes 1 or more collections and maps f over them
if you called it with no args, it would return a transducer
and the transducer would map f over its input (of whatever sort that might be)
I have a vector like this: [1 2 3 4 5 6]
and I want to turn the first two indices into a nested vector [[ 1 2] 3 4 5 6]
-> my solutions have all a little too clunky. For example:
(into (vector ((juxt first second) [1 2 3 4 5 6])) (drop 2 [1 2 3 4 5 6]))
=> (apply into ((juxt (comp vector (partial take 2)) (partial drop 2)) [1 2 3 4 5 6]))
[(1 2) 3 4 5 6]
this is a case where likely the right solution is to use a simple let block, instead of funky higher order fixed point functions
That's good to know, I figured that because I want to consistently do this to this particular piece of data that it might be best to figure out something "functional". Good to know
a let block is still functional, it just trades the elegance of anonymous application for the clarity of separating steps explicitly
how about this? @tkjone
=> (apply vector (vec (take 2 v)) (drop 2 v))
[[1 2] 3 4 5 6]
Yep! Thats certainly much nicer than mine
if you know v is guaranteed to be a vector, yes