This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-09-14
Channels
- # announcements (1)
- # beginners (85)
- # calva (23)
- # cider (3)
- # clj-kondo (33)
- # cljs-dev (12)
- # clojure (79)
- # clojure-dev (5)
- # clojure-europe (1)
- # clojure-nl (3)
- # clojure-uk (3)
- # clojurescript (5)
- # clojutre (2)
- # data-science (30)
- # datomic (3)
- # duct (7)
- # fulcro (8)
- # garden (18)
- # jackdaw (2)
- # leiningen (1)
- # off-topic (3)
- # pathom (4)
- # qa (24)
- # re-frame (13)
- # reagent (3)
- # shadow-cljs (58)
- # testing (1)
that's right, but I felt it would be natural to have a straightforward function in the standard lib
I am writing a function that "flattens" a hashmap of hashmaps by transforming the key of the inner hashmaps into a namespace: {:hello "world" :foo {:bar "baz" :ham "spam"}} -> {:hello "world" :foo/bar "baz" :foo/ham "spam"}
the purpose is to be able to treat a hashmap as an applicative functor and simply do algebra with it.
I am 99% sure that there is nothing in the Clojure core library to do this. keyword
, name
, and string manipulation functions are the building blocks that exist to do it, though, without a lot of fuss.
Note that keyword
can take 2 arguments, to create a namespaced keyword.
yeah, the detour through another type is just confusing though. unless there is something I am missing. :thinking_face:
It gives you lots of power to go through that other type, given that it already has a big set of functions that can manipulate them (i.e. strings). If you tried to reproduce some or all of those for keywords, why stop at only concatenation? Why not sub-keywords by index into the name? Why not starts-with? ends-with? includes? and regex matching on keyword contents?
You could reproduce 10 to 20 functions that work on keywords directly, or "convert to string", do whatever you want, then "string to keyword".
well I agree. I guess another way would be to write a function that makes any string function work on keywords?! It might only be a matter of aesthetic taste as to which is more readable. I'd prefer not to muck with the presented data if possible.
I am not sure if I understand your meaning, but one interpretation of your original goal is that you want to muck with the data.
you are given some collections that contain some keywords in them, and you want to change those collections to contain different keywords.
but yeah, one could write a function that takes a string function, and wraps it inside of a "convert keyword args to strings, then perform the original function, then convert the strings results back to keywords". Cool thing: you can do this yourself if you want it -- it doesn't need to be part of Clojure.
one view of software development is to build up the system you are given, layering functionality on top so it is more natural for solving the problem at hand.
yes, but I am hiding this mucking operation too. the main idea is remaining true to the natural presentation of data structures.
Sure. It seems likely that writing one or two functions that take one or two keywords, and return a new keyword, in the way that you wish for your code, could isolate the keyword->string and string->keyword conversions to those few functions.
I have a higher-order function where caching is abstracted into two arguments, fetch
and store!
. I could check if fetch
is not nil and let that represent that there is no value cached, but nil is a valid value and I don't want to recompute nil if it indeed is cached. (The problem is kind of like to how you'd use find
instead of get
if you need to distinguish between nil value and missing key.) I could require that fetch
wraps the return value in a vector or a map, or even any type of reference (e.g. atom). Or I could pass a special value as an extra argument to fetch
which represents that the value is missing, and check for that.
Which would be your preferred way?
Clojure's memoize
uses a sentinel value for that. It's a pretty common pattern. In CLJS its definition looks like (def ^:private lookup-sentinel (js-obj))
. Your fetch
function could return something like that - something that you know cannot be a part of the domain data.
I was leaning towards passing in an extra argument, so I'm going with that. (I prefer that over def
ing a value, but I can't really argue well for it. :-))
Sure. But if you see something in the core of Clojure, it's usually there for a good reason. 😉
To clarify, I meant that instead of having the value publicly as a var, I pass it as an argument. I might anyway def the value to not recreate the unique value on every call (not that I think it would matter the least in this case).
If nothing else, I don't have to name the value, the author of fetch
can call it whatever. 😉
Oh, but it's not a public def
. Note the ^:private
tag. You can also wrap your defn
s in a let
that gives the values a very narrow scope.
I just pass the value, fetch
is provided by the user. I'm one user, but not necessarily the only one. 🙂
I don't seem to find the sentinel value that you talked about. memoize uses find
on a map stored in an atom.
Another thing - if you provide a static sentinel value, you could write a function like exists?
that checks the value returned by fetch
. If it returns false
then you need to compute the value and call store!
.
in the clj-fx examples there are a few places where you feed the (deref state)
into a function and then reset!
the result of applying a function to that state
so if i have another thread updating the state and an action goes off im now no longer certain i wont lose stuff
(i can do it with swap! obviously, but its no longer clear to me how exactly effects should work)
You raise a valid concern, this is actually not ideal. I tried to emulate re-frame, which is cljs, hence, single-threaded.
You can create :swap-state
effect:
(fx/wrap-effects handler {:swap-state (fn [f _]
(swap! *state f))})
Your handler then will be like that:
(defn handle [event]
(let [{:keys [event/type text state]} event]
(case type
::add-todo {:swap-state #(update % :todos conj {:text text :done false})})))
im still a bit fuzzy on how effect handlers should work in general so i might need to read up more
i know in the simpler model of elm I would get the state as something i could consider when making a decision about effects
but lets say, as a toy example, i wanted to send an http request if my "count" is even
with the swap-state approach I can have a function that updates my state based on the "current" state of the flag no problem
but the state i consider when deciding to make an effect can be out of sync with the state i consider when deciding what my new state will be
since i would end up needing to deref the atom in my event handler to know what to return in that effects map
I have somewhat easy answer: don't swap/reset state atom you use in cljfx from different threads
I may be off base here, but this sounds like it might be a fundamental issue in any multi-threaded program where multiple threads wish to sometimes make updates to a centralized state. If they get a snapshot of the current state and compute on it for a while before doing some kind of mutual exclusion to do an update, then that snapshot can in general be "old" by the time you enter the critical section.
how to do it in a thread-safe manner:
- wrap event handler using fx/wrap-async
- don't use atom, send event maps to wrapped event handler.
wrap-async
will make event handling go through agent, so it will happen sequentially, but it will be safe to call from multiple threads
@andy.fingerhut yeah, I think agents are a good solution here
Even update functions given to swap!
can be redone N times in general, if other threads finish their swap!
calls before you do.
Well more precisely, if they update the state of the atom after you read it, but before attempting your compare-and-swap inside the swap!
implementation.
@emccue don't forget to read docs about fx/wrap-async
if you don't know how agents work, there are some peculiarities
And just to make it clear: if you use wrap-async and don't use state atom from different threads, it is safe to use deref co-effect/reset effect in event handling
im planning to use clojure for a software dev. class this semester and they have a requirement for a desktop gui