This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-02-15
Channels
- # announcements (8)
- # architecture (9)
- # autochrome-github (1)
- # babashka (48)
- # beginners (55)
- # calva (36)
- # cider (16)
- # clj-commons (1)
- # clj-kondo (38)
- # cljs-dev (44)
- # cljsrn (1)
- # clojure (164)
- # clojure-europe (35)
- # clojure-nl (2)
- # clojure-norway (10)
- # clojure-uk (23)
- # clojurescript (50)
- # conjure (24)
- # core-async (1)
- # cryogen (2)
- # cursive (38)
- # datalevin (11)
- # datascript (2)
- # datomic (13)
- # duct (1)
- # emacs (16)
- # events (12)
- # exercism (3)
- # figwheel-main (7)
- # fulcro (26)
- # honeysql (5)
- # integrant (1)
- # jobs (3)
- # kaocha (6)
- # lsp (72)
- # malli (22)
- # nextjournal (35)
- # nrepl (1)
- # off-topic (34)
- # pathom (5)
- # polylith (8)
- # portal (40)
- # re-frame (14)
- # reagent (42)
- # reitit (1)
- # releases (1)
- # remote-jobs (1)
- # reveal (9)
- # sci (2)
- # shadow-cljs (13)
- # sql (3)
- # tools-deps (33)
- # vim (25)
Hey. Does anyone know how to use test.check and generate a keyword with specific namespace? I tried this but it gives me a random namespace:
(clojure.test.check.generators/generate
clojure.test.check.generators/keyword-ns)
=>
:-i/*-:-*7B
And I want this: :my-ns/*-:-*7B
But I don't understand what are the inputs.
Also the source contains this:
(->> (tuple symbol-name-or-namespace symbol-name-or-namespace)
(fmap (fn [[ns name]] (core/keyword ns name)))
(resize-symbolish-generator))
Which could probably support the generation by hand.(require '[clojure.test.check.generators :as g])
=> nil
(def my-ns-keyword (g/fmap (fn [k] (keyword "my-ns" (name k)))
g/keyword))
=> #'dev/my-ns-keyword
(g/generate my-ns-keyword)
=> :my-ns/a5iN
The above wraps the build-in keyword
generator to create another generator that simply adds the required namespace to each generated value.
Ah, I see
Why does fmap
need to be passed g/keyword
? Because it's a generator for a keyword that doesn't contain the logic. What's the use of it?
Found it. In gen-fmap
it finds the size of data that it needs to read.
fmap
operates on a generator - so it needs some generator. g/keyword
makes the most sense because you need a keyword and that generator already generates a perfectly valid keyword. You could of course roll out your own implementation, but it probably won't bring any benefits over the built-in g/keyword
.
This is the issue I was trying to solve. I created it. I'll file a PR and mention you. https://github.com/metosin/malli/issues/642
Is anyone aware of a library that will create an HTML visualisation of a Clojure data structure? Something like the visualisation provided by re-frame-10x or re-frisk?
If you want a browser plugin to inspect data in devtools, there's https://github.com/cjohansen/gadget-inspector
If that's Cursive and it works but you want for the highlighting to be gone, then it's better to ask at #cursive
It is about Cursive. Clojure knows perfectly well what the types here are since it's all Java interop with well known interfaces.
To double-check that type hints are irrelevant here for Clojure, you can (set! *warn-on-reflection* true)
in the same namespace or REPL session and see that there are no warnings using any interop syntax.
![thanks3](https://emoji.slack-edge.com/T03RZGPFR/thanks3/868be8344387d7f0.gif)
the response message from our cloudwatch aws logs has a message key includes this as part of the string "#js {:resource "/v1/eql"}"
what protocol/format is that? how would i read it? it looks like how i would write a js object in cljs, so maybe some form of clj/read-string? is that available in cljs?
my hunch is that someone serialized a hash-map that had a hash-map as a key into json, and since json can't contain a hash-map as a key, it stringified the key data(?)
and they used pr-str
for the stringify (which tries to make something that would round trip and usually in js that would)
Planning a small personal project to create some scripts with shadow-cljs and run them via gh actions 1 will respond to a webhook, another will run a script every day at midnight. How would you go about the building? Would you commit the build to the repo? Would you have it build everytime the script runs, or is there another option?
I would try to build every time, but also use the caching options in github actions so that it will be fast. You could store the .shadow-cljs/builds (double check the name) directory, for example.
You can also package the shadow-cljs output into a pre-built docker image yournamespace/pre-built-action
if you're not needing to rebuild too frequently. As an alernative to using shadow-cljs, have you seen https://github.com/marketplace/actions/babashka-clojure ?
I have found this really useful for scripting gh actions and just checking in the scripts.
Not only have I seen babashka, I wrote an article a year ago on how to use it to build websites on shared hosts using babashka to process cgi scripts. This script is a bit more involved and I want to work with various node apis and cljs is my favorite language currently
Cool, I see what you mean. I'd be interested if you find a good way of packaging the cljs scripts - I've just been updating docker images each time I need to update my cljs based actions and that can be a bit onerous
Haven't tried it yet but it might be possible to use lumo-cljs to run the script and shadow to develop the script. With lumo you can specify the classpath dir and the src dir and it will run more like a regular clj app. The one tradeoff that comes to mind is may have to switch npm deps to (def some-api (js/require "some-api"))
@alexmiller What contract have (byte-array 16)
function? I expect to get an empty byte array 16 bytes length, filled with zeros. I got floating errors during the tests of a cryptographic hash. Hash from empty string should have a particular value according to standard. But from 10 attempts of running the same test (hash “”) only 7 were successful. I could not understand why? And then I changed (byte-array (.getDigestSize digest-class))
to (byte-array (repeat (.getDigestSize digest-class) 0))
and error disappeared.
oops, error is not disappeared. My mistake. But anyway, what contract have that function?
what you're saying sounds like using a math operation, that is reflective with overloads, and getting a random overload, which is a thing that happens right now (and is fixed by removing the reflection)
Thanks
so try (set! *warn-on-reflection* true)
Yes, I’ll try.
I would guess if you checked some of the intermediate results of the hash, you would find the difference
what kind of hash? I recently ran into a signature that wasn't reproducing right, and after spinning my wheels I realized there is a nonce part of the hmac of course
As said Ghadi, I run “error prone Bouncycastle” lib GOST hash functions 😂 They have stable results, according to standard, but not for empty string.
For empty string I got flawing error.
fixed error in this commit https://github.com/redstarssystems/gost/commit/76d95bace213d777d7f39e9c71d75fc245c57613 I don’t know why but first call of hash using this map produced an error, but subsequent calls using that map had no errors. Anyway it is now fixed. thank you.
but why subsequent calls had no errors? Only first call produced an error hash value. After first call, any N calls of the same code had no errors.
I mean
(sut/digest-3411-94 (.getBytes m2)) ;; => error
then
(sut/digest-3411-94 (.getBytes m2)) ;; no error
(sut/digest-3411-94 (.getBytes m2)) ;; no error
(sut/digest-3411-94 (.getBytes m2)) ;; no error
(sut/digest-3411-94 (.getBytes m2)) ;; no error
incorrect hash value
no exception, just different hash value
error = wrong value
But I think you are right - those functions has some state
Doing further pre-work on Clojure survey, is there a category of answer I am missing here (will also have a separate question for specific answers, don't need those, just looking at categories). "How did you learn to program in Clojure, ClojureScript, or ClojureCLR?" • Book • Online course • Online resource (other than a course) • In-person course • From a friend or colleague If additional categories, please answer in thread
The colleague and in-person course answers pretty much cover it, but I wonder if it makes sense to specifically call out an employer-based bootcamp/course as a separate category?
it might, thx
It isn't immediately obvious to me where to put a student's academic curriculum. I suppose a university class would be "In-person course" in most cases.
that's worth adding
To expand on what @U01GXCWSRMW said, I learned Clojure during a summer internship. It wasn't a specific course, merely learning on-the-job. So maybe just a generic "Employment" category could cover it? (Since it was an internship funded by the university, some might say it blurs the line between academic and the commercial world.)
Multiple choice? Or "how did you primarily learn...?"
planning to make it single, but the followup question will leave room for more nuance
“Porting a ruby app to Clojure”
(with the help of online resources and books)
Which option best matches “reading through Brave Clojure one weekend and then using it professionally for a year with colleagues who offered helpful advice”? Is there maybe a missing “learn by doing & googling” option?
Another Clojure survey question, what answers are missing here... "What was your biggest challenge getting started with Clojure?" • Functional programming • Lisp orientation • Installing an editor/tools • Getting a working REPL • Using the REPL as workflow • ... ? answers in thread please!
(Clojure beginner) — Study cohort to whom to be accountable for progress in completing sections / assignments — this is critical for developing sense of community — AND it's a super opportunity to help beginners learn the preferred way of using clojure, how to work with the tools and language, rather than against or in ignorance of them…
Going from a bunch of simple functions to a full program (without having frameworks to spell it out for me 😉) (Clojure since late-2018)
I might remember some issues but its been so long since i faced those getting started issues. Makes me think that perhaps a disclaimer on the question that you only want answers from beginners or recent beginners unless you are very sure of what your issues were.
This is part of "functional programming", but I remember really struggling specifically with immutability. I was familiar with pure functions, anonymous functions, and map/filter/reduce etc, but writing algorithms with only immutable data structures felt really awkward. I also didn't know where to put state. Which reference type should I use? Do I just put a bunch of atoms as global defs? etc.
my biggest issue was immutability as well. i have used other languages with first class functions and an emphasis on functional composition, but immutability was really tough to get my head around
For me, clearly "Getting a working REPL". The whole Clojure eco-system as second. I was quickly into FP and I love it. But after 3 years, I still don't rely on REPL driven dev. Apart from evaluating some numbers - what is it good for exactly? So mainly I sit there and think. After CTRL+S I quickly see via browser if stuff works. Clojurians may think: How sad. But reading 1000x what a REPL is didn't do it for me. No real tutorial out there.
I wonder if there are some good seeds of answers in this old thread? https://clojureverse.org/t/what-do-beginners-struggle-with/5383
some of the things in there are a little more fine-grained than what I want but thx for the pointer, some good stuff here
Less installing an editor/tools, and more getting used to them; learning how to use them. E.g. slurping my head around structural editing.
My getting started was a long time ago, and lost to the mists of time. But I’m talking a friend and ex-colleague through getting started with Clojure and ClojureScript right now, and what he’s struggling with is tooling. Not so much how to install tools, but which should he even start looking at: Should he use Figwheel or Shadow? And how do they relate to deps.edn? Where does Leiningen fit in?
Coming from a mostly Java background, I struggled a bit with modelling: what to reach for when (mentally) converting a domain entity (such as a Person instance) over to Clojure data.
immutability for me. I had a really hard time modeling complex data transformation as a series of input->output steps, rather than massaging my data like play doh
"Should he use Figwheel or Shadow? And how do they relate to deps.edn? Where does Leiningen fit in?" -- I'll just ➕ this because those are very common beginner questions (`lein` vs clojure
across the board, Figwheel vs Shadow for cljs). Beginners definitely get confused when one book or tutorial starts them off with lein
and project.clj
(and plugins, argh!) and then another book or tutorial starts them off with clojure
and deps.edn
. It's not something the core team can reasonably answer, but it is indicative of some of Clojure's approach to "composing tooling/libraries" where you see beginners asking "which web server? which web framework?" (and, to some extent, "should I use Spec or Schema or Malli?"). Not sure how to condense that down to a challenge category... "Choice of tooling/libraries is too broad/confusing, with no preferred/"officially recommended" choices"? Something along those lines.
^ This is totally what I meant; like if you start webdevelopment in Python or Ruby, you get told to use Django or Rails, with all their migration/task running/tooling…. But in clojure its compose-your-own-tools, so now I need to know what component framework I want, which webserver, which router, how to do migrations, …. (Not saying that this is a bad thing, on the contrary, but a mind shift)
not sure how to word it, but people can stumble on java as well (the ecosystem, the tooling, translating examples from java to clojure, etc)
• Lein vs. Boot vs. clj
? Some tutorials assume one or the other. Translating between them is a bit difficult.
• Stack traces.
• Unfamiliarity with the Java ecosystem.
• Unfamiliarity with Java conventions (e.g. resources
directory, JVM settings)
• Lack of opinionated web frameworks. As a beginner, I kinda want to be told what to do. I need a golden path to success before I'm comfortable on 'choosing my own libraries'.
Starting with Calva in VSCode, I was pretty confused by the idea of developing using the REPL. The "jack-in" thing was strange. I wasn't sure why or when I should "load" a file. And how/when should I just run a program all the way through?
getting trapped in a 2-year configuration cul de sac by emacs
Tooling, it was definitely the tooling. I think things have gotten much easier now, installing clojure in WSL and VSCode with Calva is a pretty flawless setup, and easy. BUt getting it working on windows I remember being really hard and having lots of issues (although now I can't remember what those issues were). Also when I started I was using lein, I found it hard to get my head around lein and creating new projects, how these were meant to be setup. I Come from .NET where creating a new project means opening either VS or Rider and then selecting the type of project I want. F5, it builds and I have an .exe I can run. Compared to this experience Clojure felt so difficult and at the time I felt the documentation was awful. I also didn't know if I should use project.clj and lein or deps.edn... Honestly, even now I mostly just copy an existing project folder when I want a new project 😄 I still find deps.edn pretty confusing and try to stay away from the Clojure CLI as much as I can. When I started wanting to do CLJS, do I pick Figwheel, Figwheel Main or Shadow-cljs (I finally landed on shadow-cljs and now feel prett comfortable with it), but at the time, god, there was so many choices for how to structure a cljs web app. Too many choices and I had no idea what to choose. One of the reasons I wanted to do CLJS rather than conventional web dev was to get away from the insane tooling complexity... Then when started I felt I had walked into a whole different world of complexity. Clojure error messages and stack traces were also really hard as a beginner, but then I hadn't done any Java really. So maybe these are more familiar to Java developers. Also, not coming from Java, Java ClassPath stuff was a total mystery to me.
Also at the start, every tutorial I read said you basically need to use Emacs. When you have IntelliJ Cursive and VS Code Calva I don't think that's true at all!
For me it was the editor/tools. Although I had used emacs before, it was too much of a distraction and learning curve for a beginner. Vim is also out of the question for a beginner (or most other people). My best experience was with Clojupyter, because I could intersperse code with commentary, and paste text/images from books I was reading. It's not a project development tool, but it's a superb learning tool. I have used Jupyter to learn several other languages. Installation using Anaconda on Windows is hair-raising though, because of Java version conflicts. It needs some attention, but it's great. The books/articles available on Clojure are much more mature in their approach than a lot of other programming books I've read, so a lack of learning materials is not a problem. The other IDE I like is Visual Studio Code with Calva - not as good for learning, but allows project development. 4Clojure was a great help - beginners need more problems on sites like that.
If you don't come from java land the error messages aren't helpful for a newbie. One issue I had initially I learned about data type, assign "variables", basic math, how to create function, then when it came to looping it wasn't easy to figure out. Haven't found a really good resource for actual beginners to learn clojure. Always thought 4clojure, katas, koans ... are terrible for new people considering there's no clues as what you should do, error messages are cryptic, and don't know the terminology to ask the right questions so all you can is google the answer. Then you find dozens of different answer and no way to evaluate which is "better" or why.
I am currently in the midst of learning Clojure. I have some experience with functional concepts and OOP, but always statically typed. For me, currently, the biggest challenge is avoiding type errors all the time. While I rely on the REPL quite a lot to build up my code in tiny steps, I often find myself having type mismatches. The error messages are not helpful for me in many cases and it can take me quite a while to figure out what the actual issue is. So: finding a reasonable workflow without static typing is definitely a challenge.
@U032VA6PE9H Can you be a bit more specific about what you mean by "type mismatches"? What sort of errors are you running into that you think a type system would catch?
Feedback I received from a very experienced Java developer is a lot of friction with the workflow Clojure requires and encourages to be effective. He doesn't even work with the REPL, and complained about start up time when running tests. On one hand "you're doing it wrong", on the other, why is the " right thing" hard?
@U04V70XH6 Good question! I think it's usually a matter of nesting. Do I have a thing? Or the sequence of things? Or the sequence of sequences of things? But I will try to collect an example or two when I come across them as I struggle to remember the specific cases. I often find myself thinking that it would not have happened in a statically typed oop language where you can "dot into" the object and see what is available. If this does not fit your expectation, it is very easy to check what you actually have, type-wise. But this might also be explained by the fact that I have vastly more experience working that way.
lack of familiarity with Java/JVM was the big one. Without that background I found it very hard to understand certain things, especially around protocols and records. Also just the idea that there is a piece of runtime state called the “classpath” that is very opaque in its derivation and controls what I can and can’t require.
@U04V70XH6 Today I got (un)lucky and ran into one of these situations again where static types would have helped me find the issue quicker - I think. To learn Clojure I am working through the last adventofcode, currently challenge 4. I was working on parsing the input file into something I can then process more easily. The basic idea is of challenge 4 is: you are playing Bingo, and the input is a set of values and some bingo boards. I had working parsing code that I wanted to refactor a bit and then got stuck - it was not working anymore and I took me quite a while to figure out where the issue was exactly. First, the code that was working:
(def example-input ["1,2,3" ; these are bingo moves
""
"1 2 3" ; this is the first bingo board
"4 5 6"
"7 8 9"
""
"9 8 7" ; this is the next bingo board
"6 5 4"
"3 2 1"])
(defn parse [input]
(let [not-separator? (partial not= [""])]
(->> input
(partition-by string/blank?)
(filter not-separator?))))
;; moves is a single element array
(def moves&boards (let [[[moves] & boards] (parse mini-example)]
{:moves (line->moves moves)
:boards (map strings->board boards)}))
line->moves
and strings->board
then just parse the strings into a 1- and 2-dimensional array, respectively. Simple enough, but I disliked the nested destructuring in the last function, the [[[moves] ...
part. So I moved the "extraction" of the single value into the parse
function. I was happy that I even found a usage for the mysterious juxt
function!
(defn parse [input]
(let [not-separator? (partial not= [""])]
(->> input
(partition-by string/blank?)
(filter not-separator?)
((juxt ffirst rest))))) ; added this line
; able to remove the nested destructuring, yay!
(def moves&boards (let [[moves & boards] (parse example-input)]
{:moves (line->moves moves)
:boards (map strings->board boards)}))
Unfortunately this did not work the same way as before the refactoring, oh no! Figuring out that I had nested the boards
in an additional sequence via the combination of rest
and &
took me an embarrassing amount of time. A static type checker would probably have made it easier as it could tell me immediately "`boards` is nothing you can map via strings->board
". Then I would have checked what the expected type is, compared against the actual type, and that would have been it.
But I first thought that something is wrong with moves
- that was what I had changed after all - and was searching and searching in that direction. In the end a bunch of println
statements in various places gave me the clue I needed.
I hope this example was a bit more specific and understandable :)@U032VA6PE9H I'm guessing you weren't evaluating each small change and testing it in the REPL as you went?
(comment
(parse example-input) ; evaluating this each time you change parse
)
It takes a while to get into a good flow with the REPL -- I think teaching REPL-based workflows is something we're not doing well at yet in the Clojure work -- but it's that sort of thing plus, perhaps, judicious use of Spec during development, that help avoid this sort of error. When I first ran your (working) parse
on the example-input
, my first thought was "that's a weird result, why not return {:moves "1,2,3", :boards [["1 2 3" "4 5 6" "7 8 9"] ...]}
instead of a vector where the first element is special/not the same kind?"(now, I can see an argument that you wouldn't have defined a type that was a vector of a single-element sequence containing a string followed by one-or-more sequences of strings representing boards -- but that thinking should happen whether you have type declarations or not)
My biggest challenge at the beginning was: developing muscle memory to type naturally using a prefix syntax, after over a decade on the job using only infix syntax.
Even if I was very motivated and excited about Clojure, even if basic concepts were clear and so on, I took at least a couple of months working full time to definitely eliminate the episodes of shooting myself in the feet with random stuff.method(blah)
coming from autopilot.
(now I can proudly say that, sometimes, colleagues raise an eyebrow when my code vaguely resembles Lisp, even in non Lisp languages 😎)
@U04V70XH6
Thank you for showing interest in this topic 🙂
Thanks to your responses I reflected on it again. I think I understand now better why I did not see the actual issue.
I already heavily on the REPL during development. This is what I love most about Clojure at the moment! It is of tremendous help especially for a beginner as I can just try out unfamiliar functions and build up my own step by step. But here I struggled to apply it properly as I already had a "bigger" piece of code which I refactored. While the amount of code is still laughable and the change was tiny, it still was not this "add tiny steps one after another" that I can do when writing a new function.
In addition to that the returned value of my changed parse
method fit my idea of what I should get. I wanted one string for moves, and then a collection of multiple strings per board. And this is what I thought I still see when evaluating the changed parse example-input
. It did not occur to me that the "boards" were now wrapped in an additional collection, because it fit my mental model of "one string for moves and then all the boards". On top of that the destructuring was adding one more level of nesting, but this was not something I could easily see because it happened "inside" the moves&boards
function, and was not part of a result - so I did not "see" it in the REPL when evaluating any of the functions.
I think what I will do next time I find myself in a similar situation:
• Instead of calling another function inside a function, return the value I would pass to that function
• Safe that somehow (in the REPL output or via a def
)
• Call the next function myself with that saved value - as I can now separate it into individual steps again
;; example
;; If this does not work
(defn some-fun [a b c]
(other-fun (do some stuff with arguments))
(defn other-fun [x]
(b (a x))) ; BOOM
;; Try this
(defn some-fun [a b c]
(do some stuff with arguments)) ; removed call to other-fun
(def something-wrong (some-fun 1 2 3))
(other-fun something-wrong) ; BOOM again
;; Now I can do the individual steps of other-fun
(a something-wrong)
(b (a something-wrong))
...
So I have a question about contributing to core 🧵
There's two functions that I need basically constantly that feel a lot like they're core functions that are missing, they are assoc-some
and update-some
, which are functions that will add or update a value in a map if one exists, and if they end up associating in a nil value they remove the key.
My question is this: is this a realistic thing for me to consider putting in a ticket in jira as a proposal with a patch, or is it a pipe dream to think that something like this would be added?
I realize the turnaround time on it is likely to be measured in six months to years because it would likely be at earliest considered for the next release, seeing as we're in beta for this release.
But I'm curious what others' experiences are, and even what Alex Miller thinks if he's around and willing to discuss, about what this looks like.
(They are in medley btw, which is a library I often include for these kinds of functions)
the particular usecase for these functions is to make it easier to work with spec because it's very common to want to put optional fields onto a map, but doing so with assoc
and update
is likely to cause an object to no longer conform to its spec because the spec has optional, but not nilable, values for some key.
@UDF11HLKC that's good to know, but definitely too heavyweight for the problem that made me want to ask the question in the first place.
some things: • here in slack, #clojure-dev is a channel for dev of Clojure itself, so best place for this thread we're in (no sense changing now, just fyi) • would prefer to start anything like this in http://ask.clojure.org (where you can possibly find prior open requests and vote and/or add to them) • we are willing to consider adding things (see update-keys, update-vals in 1.11 as good examples). information to make a case: a) is this in keeping with clojure philosophy in terms of genericity, polymorphism etc b) what are the workarounds - is it in a lib, is it hard to write or easy to write badly c) if it exists in a util lib, provide usage data to make a case for putting in core - include some http://grep.app links or grape searches etc d) is there some potential extra benefit from having it in core (backed by an interface with custom methods in impls) for example
and then see if people are interested and vote on it (update-keys and update-vals were in an ask question with dozens of votes)
we look at stuff people vote on
Thanks for all the pointers! I was in the middle of making an http://ask.clojure.org question, I'll make sure I look at some of those tools to see if it's used a lot and implemented a lot. I'm also including the primary usecase and the pattern that it makes arise for people who want to write correct code.
Thanks for the feedback!
getting into the details of what to do in special x/y/z is probably not critical, but listing the places where decisions need to be made and an enumeration of options is extremely useful. if there are multiple impls in different util libs, that can be a good place to explore some of that
thanks
and don't get tied to the names, because Rich will probably have his own opinion :)
Yeah, I don't really care about the names, I just care about the fact that the easy thing to write, assoc
, makes things fail specs if you don't do careful testing, and you instead have to do weird stuff with cond->
Maybe there's a better solution to the problem too that someone else will have, but at the moment the solution I have found with cond->
just feels far from ideal.
trying to be as clear as possible about the problem driving the request is the most important thing you can do
so don't skimp on that :)
Yeah, what I'm writing at the moment is focusing on existing patterns and fixes and why I think they are bad, the solution I propose is probably mostly going to be a footnote.
sounds good
I'll be curious to read the use case around update-some
-- I'm having a hard time imagining the semantics (`assoc-some` seems straightforward and I reach for cond-> .. (some? v) (assoc :some-key v)
a lot for that sort of stuff).
I'll admit I've only used update-some
once and I don't think it's critical at all, so I don't think I'm likely to mention it specifically in the ask.
yeah, medley calls it update-existing (and update-existing-in)
https://ask.clojure.org/index.php/11584/common-patterns-optional-fields-interact-poorly-with-spec
I feel like I could've come up with a better conversation starter/question at the tail end of that, but I felt like I put enough work into it to have gotten my thoughts across.
Upvoted. There are definitely places in our code where some sort of assoc-some
would help clean things up -- and not just around non-nilable optional fields in specs. We have fewer places where update-existing
would apply because, in a lot of the cases, we want the function result added but we just need a default instead of nil
(so we use fnil
).
I'd also like to point at https://github.com/IGJoshua/farolero/blob/master/src/cljc/farolero/core.cljc#L503-L516 as an example usage of update-some
, because I'm getting a map in and trying to turn it into a ::restart
value, and one of the keys which is renamed may be false and in that case I want to omit the thread entirely.
false
!= nil
-- and (some? false)
is true
Yes, I'm aware. That's the intention here, it's taking a false value and deliberately turning it to nil in that update
call, and when it returns nil
it would remove the key.
when you are modeling subscriptions of some kind you often want something like a map from topics to sets of subscribers, and to remove a topic from the map when the set of subscribers is empty (we have a few examples of that kind of thing) which something like update-some might be good for
I think there was some previous discussion of a dissoc-in that removes empty submaps as it goes, which is a similar kind of thing
Yeah, there's something like that in com.rpl/specter
which might be worth looking at, but yeah, having a way to do an update on a map that removes the key feels like an important primitive.
https://clojure.atlassian.net/browse/CLJ-1063 is the dissoc-in ticket
for the reasons in my comments in that ticket, I no longer think we should add dissoc-in to core
that's fair, although I'll admit that the connection between dissoc-in
and what's being discussed is tenuous at best.
for sure :)
^Somewhat related: I sometimes write (update m k #(or % v))
, which is kinda like update-not-some. Or; more like an assoc if not exists
a common pattern for that is (merge {k v} m)
although I don't know what the performance characteristics are of this pattern.
yeah, that makes sense, although that doesn’t work with the update-in variant
The merge
approach will also change the type of the map, which sometimes is not acceptable.
Perf of that is probably not great, will re-build the second map mostly (but for small map, prob doesn't matter)
user=> (require '[clj-time.core :as t])
nil
user=> (t/today)
#object[org.joda.time.LocalDate 0x6932266f "2022-02-15"]
We migrated off it at work (and as maintainer of clj-time I advise everyone to do the same!).
not the worst hole, but we've had some annoying jdbc time resolution bugs im not super excited to solve again
Since it's pulling joda time, (org.joda.time.DateTime.)
should do it, provided you've set the TZ for the process somehow (jvm props or TZ
env var) or if you want to use the system timezone
in java.time it would also be (LocalDate/now)
, no? so t/today
producing a (joda) LocalDate
sounds correct