This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-09-20
Channels
- # 100-days-of-code (2)
- # aleph (53)
- # architecture (2)
- # aws (3)
- # beginners (230)
- # boot (15)
- # calva (3)
- # cider (19)
- # cljs-dev (1)
- # clojure (139)
- # clojure-conj (3)
- # clojure-italy (47)
- # clojure-nl (19)
- # clojure-spec (26)
- # clojure-uk (98)
- # clojurescript (152)
- # clojutre (4)
- # core-async (22)
- # cursive (5)
- # datomic (48)
- # emacs (11)
- # events (1)
- # figwheel-main (219)
- # fulcro (15)
- # instaparse (3)
- # jobs (4)
- # jobs-rus (1)
- # leiningen (30)
- # luminus (8)
- # off-topic (67)
- # onyx (5)
- # pedestal (16)
- # re-frame (1)
- # reagent (4)
- # reitit (31)
- # ring (8)
- # ring-swagger (3)
- # shadow-cljs (115)
- # specter (4)
- # videos (1)
- # vim (20)
- # yada (15)
Oh and just to be clear, in case that was directed at me, no worries; I realize that ->
is not, say, a pipe function that takes functions and composes them to return a new function like a reversed comp
would, but a macro that builds up an actual expression from its unevaluated arguments (er .. not to be redundant, considering its a macro). When I wrote, say, "the comp
/ ->>
" way", I was mentioning the use case they have in common, namely composing functions, but I don't mean to imply that they are themselves the exact same or that they achieved this result in the same manner, nor that the arrow macros could not be used in other ways.
when I call println from multiple threads the result is all garbled, what's the best way to address this?
@roklenarcic a simple way to address this, with agents :
(def safe-println (partial send (agent nil) (fn [_ & args] (apply println args))))
the best way IMO is to use a real logger, such as https://github.com/clojure/tools.logging or https://github.com/ptaoussanis/timbre
obviously an actual logger takes care of this
thanks for suggestions
Thank you @U06BE1L6T
Hello. I have this small problem designing the architecture for one of the modules. I have 2 modules - game and round. A game is composed of multiple rounds in addition to some other meta-data. There is only active round in a game at any point of time. Any action in a game is delegated directly to the currently active round in the game at that point in time. The code looks something like this
(ns com.game)
(defn do-action [game-id action params]
(let [game (from-atom game-id)
round (curr-round game)
handler (partial debit-points game-id)
new-round (do-action round action params handler)]
(-> (from-atom game-id)
(set-curr-round round)))
The problem is that I store the game in an atom. A request from the client comes with the current game-id, I extract the game from the atom using the game-id and then run the logic on the round. But the handler is a partial which can change the data in the game. Now I'm handling this by loading the game twice from the atom. But is there a better functional way to handle this?I would define do-action
to also take the game(state) as a non-atom. Probably shouldn't use any atoms in do-action
Like (do-action state game-id action params) => new-state
so the game state is a function of a sequence of actions
So the handler
can actually either change the game
object or make a http call. So was not sure how it can send the game object back
@luke.deveraux the handler
modifies the atom directly? If so, it shouldn't, but it should return the modified game data, so you have an updated copy of it.
So the handler
can actually either change the game
object or make a http call. So was not sure how it can send the game object back
Change the game atom or just the map?
(ns com.game
(:require [com.round :as r]))
(defn do-action [game-id action params]
(let [game (from-atom game-id)
round (:curr-round game)
handler (partial debit-points game-id)
new-round (r/do-action round action params handler)]
(-> (from-atom game-id)
(assoc :curr-round new-round)))
@andreas862 @mdallastella I do not want the round to be able to reach into game and and make changed; so i'm using a partial
here - But then how do I ensure that the game map is correctly handled inside round?
I think that you should try to make a completely mutation free version of your do-action function. (f state action) => new-state
Eventual side-effects like updating an atom or a score somewhere via http can be done by inspecting the new-state.
Score is probably a part of the game-state from my point of view - Your mileage might vary
Right ... that is what I started it as (f state action) => new-state
will give me a neat finite state machine which can be easily composed. But the problem is how do i make sure that the round module needs to mutate it's parent (in this case the game via handler
) - how do i structure that?
oh so you mean you want me to put in instructions that require modifications of the parent as data, and then let the parent run through those modifications?
Could you share an example of your game-map?
{:id 123
:curr-round-key [:rounds 1]
:rounds {1 {:id 673
:clicks {:total 5 :remaining 3}
:points 5}}
:lives {:total 3 :remaining 2}
:points 45}
So generally I have this :curr-round-key
which is a cursor / pointer to the actual round which is is accessed via (get-in game (:curr-round-key game))
is :points
just the sum of all :rounds/points
?
Then you could update the points in the parent using the round-map
Yes :points
in game
get updated after a round ends. However, a round starts only if the player has :lives
remaining
I had the rule in mind "str is for humans, pr-str for machines". But now I can't come up with an example that doesn't roundtrip (= (-> v str read-string) v)
. Can anyone think of a v
that breaks that cycle?
good one
A simple string will (def v "foo")
@manutter51 true, I tried ["foo"]
but not "foo"
anything other than bare strings?
Hmm, reader tags?
(def v #inst "2018")
=> #'user/v
(= (-> v str read-string) v)
=> false
(= (-> v pr-str read-string) v)
=> true
or whatever they’re called.
read-string is the dual of pr, not str
@alexmiller that's what I wrote, right?
I was only wondering if there's any example, other than bare strings, for which str is not an identical substitute
str does toString type stuff so anything where that’s wrong like a record instance
user=> (defrecord R [a b])
user.R
user=> (->R 1 2)
#user.R{:a 1, :b 2}
user=> (str *1)
"[email protected]"
user=> (pr-str *2)
"#user.R{:a 1, :b 2}"
@manutter51 that's not a great example - it's expected that it normalizes insts
Well, you didn’t ask for unexpected examples 😉
@alexmiller gotcha, yeah that makes sense
@manutter51 I take that back, that actually does surprise me, good point (I thought I'd checked that)
pretty much any Java object that’s not a Clojure type is likely to be different in this way
as str will not use the printer (so you just get .toString) but pr-str will use the printer
yeah, although regular Java objects like (java.util.HashMap.)
won't roundtrip anyway, with pr-str or str
well that example will, but using Clojure maps instead, so depends on what level of abstraction you’re talking about
oh yes I forgot that (= (java.util.HashMap.) {})
hey guys, i have this code that i hate!
(defn set-log-level [level]
(case level
"WARN" (.setLevel
(org.slf4j.LoggerFactory/getLogger (Logger/ROOT_LOGGER_NAME)) Level/WARN)
"TRACE" (.setLevel
(org.slf4j.LoggerFactory/getLogger (Logger/ROOT_LOGGER_NAME)) Level/TRACE)
"INFO" (.setLevel
(org.slf4j.LoggerFactory/getLogger (Logger/ROOT_LOGGER_NAME)) Level/INFO)
"DEBUG" (.setLevel
(org.slf4j.LoggerFactory/getLogger (Logger/ROOT_LOGGER_NAME)) Level/DEBUG)))
do you guys have any idea how i can use reflection to create the level/“level var”?
(eval (symbol "Double/POSITIVE_INFINITY"))
will return ##Inf
but i would hate that code more than what you have here
you can keep the matches in map {"WARN" Level/WARN ...}
and use that to collapse all these cases into one path using the results of the map lookup
lift (org.slf4j.LoggerFactory/getLogger (Logger/ROOT_LOGGER_NAME))
into a let binding outside the case
Final code:
(defn set-log-level [level]
(let [logger (org.slf4j.LoggerFactory/getLogger (Logger/ROOT_LOGGER_NAME))
level {"TRACE" Level/TRACE
"DEBUG" Level/DEBUG
"INFO" Level/INFO
"ERROR" Level/ERROR
"WARN" Level/WARN
"OFF" Level/OFF}]
(.setLevel logger (get level (clojure.string/upper-case level) Level/INFO))))
I would probably pull the map out:
(def log-levels {"TRACE" Level/TRACE
"DEBUG" Level/DEBUG
"INFO" Level/INFO
"ERROR" Level/ERROR
"WARN" Level/WARN
"OFF" Level/OFF})
(defn set-log-level [level]
(doto (org.slf4j.LoggerFactory/getLogger (Logger/ROOT_LOGGER_NAME))
(.setLevel (log-levels (clojure.string/upper-case level) Level/INFO)))))
then use the literal map as a function
nothing wrong with the former code though, just preferences
the reason why I thought would be ok to keep it inside the function is because it’s only relevant in this context. I mean I will not use it anywhere else.
a defn can be in the body of a let form if you like. but this is for sure bikeshed at the moment 🙂
please no… :) better to inline it
I just like letting data be data :)
one interesting thing to notice is that if you invoke log-levels like a function you can actually replace it with a function later if needed and the caller doesn’t change.
@alexmiller what you have said makes lots of sense. I will change 🙂
now you’ve gone and turned data back into code :)
seriously though, that’s clever and useful, particularly if you want some immunity to an expanding enum set over time. for this particular case, I’m not expecting that set of log levels to change and I find the literal map far easier to understand and maintain
I use it all the time for interop. For logging sure, not so useful but with say a db client lib that evolves over time this saves me the headache of tracking enum changes and error handling for them
Clojure design around this stuff all largely predates Java enums. Ideally we would have a little more automated interop here.
That one is the one I use in practice: https://github.com/mpenet/commons/blob/master/src/clj/qbits/commons/enum.clj#L40
I'd love a small editor that just did parinfer exactly like protorepl, but was compiled down to a graal native binary and launched super fast.
but graal == no eval, so no repl? (maybe I’m not up to date on graal capabilities)
that ain’t living :)
terminology around this stuff is super confusing -- graal is a compiler like hotspot. What people usually mean is 'native image' -- which is the output of Graal plus this thing called SubstrateVM
you can plug graal a normal JVM to override hotspot -- and it's the Normal Java experience. Dynamic loading, etc.
It'd be nice if they could combine the magic and get a fast booting native image that transitioned to a runtime optimization strategy as per regular JVM, after it's launched.
Hey does anyone know off the top of their head if sort-by
before group-by
helps with performance? Thanks
My guess is probably not, but does affect the order of the groupings: https://github.com/clojure/clojure/blob/clojure-1.9.0/src/clj/clojure/core.clj#L7066 ...
no, probably the reverse :)
In terms of big-O notation of run time, I don't see how it can. In terms of being faster because of cache locality in the group-by, maybe?
group-by returns a map, which is unordered
so you should not have any expectations about the printed order of the keys in the result
@alexmiller I mean it will effect the grouped items (the values to the map)
So if you run a sort-by
before a group-by
with the same f
then you can expect the grouped-items to be sorted basically..
sort-by
uses stable sorting, so with same f
why would it change the order?
Sure, but because sort-by
is stable, it should leave all elements with same f
/ keyfn
in the same order they were relative to each other in the input.
@andy.fingerhut given a sorted list, group-by will conj
each element of the list onto the vectors of each grouping. So group-by will conj
each element in the original order basically. Does that make sense? I am not sure I understand your point about sort-by
being stable... we might be talking about the same thing. I don't think group-by
would change the order - I think group-by
will preserve the order...
You said earlier "but does affect the order of the groupings", which I thought meant "does affect the order of the vectors that are values in the map returned by group-by".
Sorry I was trying to say running sort-by
before group-by
effects the order of the final groups... which is easier then running sort-by
after, for each group...IMO
got it. group-by doc string does promise that.
on the other hand, if perf is a factor, it's probably going to be cheaper to sort each group after group-by, at least if you have a large number of items and decent group distribution. though probably better to go with whatever's cleaner in context over the perf difference, at least for starters
an experiment could validate for sure, but my guess is that it's probably faster to sort the whole list then group it
:thinking_face: it seems like there’s a lot more community work around supporting datomic pull syntax than full datalog