This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-17
Channels
- # ai (1)
- # announcements (1)
- # aws (38)
- # babashka (25)
- # beginners (84)
- # biff (11)
- # calva (58)
- # clerk (14)
- # clj-kondo (14)
- # cljdoc (9)
- # cljs-dev (2)
- # clojars (2)
- # clojure (93)
- # clojure-czech (2)
- # clojure-dev (13)
- # clojure-europe (19)
- # clojure-nl (1)
- # clojure-spec (13)
- # clojure-uk (2)
- # clojurescript (6)
- # conjure (1)
- # core-async (9)
- # cursive (12)
- # data-science (7)
- # datahike (47)
- # datalevin (10)
- # datalog (3)
- # datomic (35)
- # emacs (3)
- # events (4)
- # fulcro (49)
- # gratitude (7)
- # humbleui (1)
- # hyperfiddle (42)
- # jobs-discuss (19)
- # kaocha (5)
- # lsp (20)
- # malli (3)
- # meander (2)
- # membrane (2)
- # off-topic (22)
- # pathom (2)
- # polylith (14)
- # practicalli (1)
- # rdf (3)
- # reitit (2)
- # shadow-cljs (11)
- # squint (3)
- # tools-deps (32)
- # vim (9)
- # xtdb (16)
Hello everyone, I would like to ask something about Datomic Cloud DB relations. Is there any channel based on Datomic? Or should I ask here?
What is the asynchronous programming model most common in Clojure web server development? (Particularly in Ring, since that seems to be the de-facto standard library for HTTP server interface.) Is it futures, promises or something else?
from here - https://github.com/ring-clojure/ring/blob/master/SPEC
== Handlers
Ring handlers constitute the core logic of the web application. Handlers are
implemented as Clojure functions.
...
An asynchronous handler takes 3 arguments: a request map, a callback function
for sending a response and a callback function for raising an exception. The
response callback takes a response map as its argument. The exception callback
takes an exception as its argument.
A handler function may simultaneously support synchronous and asynchronous
behavior by accepting both arities.
I think the question is how developers most commonly deal with the asynchronicity in their apps built atop a Ring library? rather than how Ring itself deals with async?
@U90R0EPHA Yes 🙂 but actually I'm asking about both: 1. How does Ring handle asynchronous tasks? (multi-threading paradigm, concurrency model) 2. How do developers perform asynchronous tasks (e.g. reading/writing from database) and how does it fit with the Ring's model?
Given the multi-threaded nature of web servers on the JVM (e.g., Jetty -- the default for Ring), I suspect folks don't use the traditional async model as much as in other languages... but I'll be very interested to hear what folks have to say about this...
With (keyword "string")
I can create a keyword from a string but not all strings that I can put in will result in valid keywords. Is there a function to make the string "save" for use in the keyword function?
no, you can try to create a regex to validate the string using those rules - https://clojure.org/reference/reader#_symbols but in general there is no such thing like invalid keyword, the problem only in its textual representation
if your created at runtime keywords will be nether serialized - you're good
Thank you, very insightful. The keywords need to be serialized because they are part of a xtdb document. I'll consider my options.
(require '[clojure.edn :as edn])
(defn valid-kw? [kw]
(try
(= kw (edn/read-string (pr-str kw)))
(catch Exception _ false)))
(valid-kw? (keyword " foo"))
;; => false
(valid-kw? ::x)
;; => true
I've been playing around with Selmer a bit to get a sense of how it compares to Hiccup, and one thing that bugs me a bit is linting in VS Code: it tells me (understandably) that {{...}} is an error (and also there's no syntax highlighting for tags). Does anyone know of a good extension or other solution for this problem?
Yeah, as someone who uses Selmer a lot, the "red squigglies" everywhere can be a bit distracting...
It is mostly confined to embedded JS code tho', if you're talking about .html
pages as Selmer templates.
If you're writing code templates for deps-new
, you run into a similar problem (it doesn't use Selmer, but it uses similar Moustache-like syntax for variables).
@U04V70XH6 Do you prefer Selmer of Hiccup, or are you forced to use it? Just curious as someone who has only used Hiccup.
For HTML pages, our UI/UX folks have to be able to work on them so Hiccup is not an option at all.
Also, for our HTML emails -- for the same reason.
We do use Hiccup for some things: XML generation, and some HTML fragments that get embedded into some very heavily data-driven HTML pages (the main page is Selmer but certain parts are generated from data so it's easier to just have a single <div>{{stuff}}</div>
in the template and then build stuff
from data using Hiccup).
Nearly all our UI is React.js these days tho' -- as far as our main, customer-facing stuff is concerned.
Thanks all. That's a bit of a bummer, but such is life I guess. Hmmm how heavily "inspired" by Django is Selmer? Would a Django extension help?
Selmer has a pretty limited and straightforward set of parsing rules. I don't think it would be a particularly large job to write a Textmate grammar for it, which could inject itself as an embedded language. https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide#injection-grammars
If you decide to take a crack at it, this will let you test Oniguruma regular expressions, which is the variant used for Textmate grammars. https://rubular.com/
@U90R0EPHA interesting. I'll let you know if I get a chance to fiddle with it.
@U04RG9F8UJZ I just installed Django Templates into VS Code and that seems to have cleaned up a lot of the red squiggles in our HTML pages (I haven't checked extensively yet).
@U04V70XH6 haha that did it! To be seen if there's any edge cases, but at least for my current usage, the errors are gone. And since the tags are basically the same, the snippets are a great bonus 👍:skin-tone-2:
Hey guys, I have question about the terminology of transducers. I have this one:
(defn my-transducer [search-term xf] ; <------ [1]
(let [regexp (re-pattern (str "(?i)\\b" search-term))]
(fn ; <------ [2]
([] (xf))
([acc l1-item] ...)
([result] result))))
• [1] I named the argument that would be conj
or other fn xf
just as I saw it in the documentation. What does xf
stand for though?
• [2] Is there any standard name for the inner function of the transducer? I'm asking because in my tests, I do actually call (my-transducer "book" conj)
, but I don't know how should I call it, I don't want to call it a transducer, since transducer is my-transducer
and this is ... like a transducer body? I don't know.
There are only two hard things in Computer Science: cache invalidation and naming things. --- Yep, exactly that!From https://clojure.org/reference/transducers,
> The transducer xf is a transformation stack ...
I've always pronounced xf
as transform, but I'm trying to figure out if that's something I made up or if it's a widely accepted way to pronounce it.
Oh good link. Yeah xf
I say transformer (should be noun, although transformer sounds a bit funny, but it's something like that.
There's also: > A transducer (sometimes referred to as xform or xf) is a transformation from one reducing function to another:
Now I want to start calling transducers transfducers
Hey @U024VBA4FD5, if you are ramping up on transducers, I personally found https://youtu.be/r6t6jsNBZDo to be very helpful.
@UE21H2HHD oh thanks! Yeah transducers took me a LONG while to even dare to get into. I managed to write one fairly complex one even, but I still don't understand many things. Now onto testing that fucker, that's gonna be fun!
Is that what the f stands for in rf? Yeah, that vid helped to realize they are simpler than I thought.
I think the best sentence that made them click for me was > A transducer (sometimes referred to as xform or xf) is a transformation from one reducing function to another: and that’s why it’s clear that [1] should be rf.
from https://clojure.org/reference/transducers ’s reference on transducers
So which one is rf
? @U050ECB92 says [1] (which would be say conj
), @U7RJTCH6J says [2].
I think rf
as reducing function should be [2], correct?
“a transducer is a transformation from one reducing function to another”. You are taking a reducing function ([1] is rf) and returning a new reducing function
@U11BV7MTK OK that's a good quote.
keep it front and center while thinking about things. i’ve found it helped quite a bit
I think one of the confusing things is that normally, your transducer would look more like:
(defn my-transducer [search-term]
(fn [rf]
(let [regexp (re-pattern (str "(?i)\\b" search-term))]
(completing
(fn [acc l1-item]
...)))))
Notice the extra level of inner function nesting.also helps explain the behavior of manually composing them like (reduce ((map inc) +) 0 (range 4))
. at first that stuff looked so strange to me. But remembering that a transducer (map inc)
is a function that takes a reducing function and returns a new reducing function it makes it very clear to read
@U7RJTCH6J right, that does make more sense actually.
@U11BV7MTK that's very cool.
Another confusing thing is that functions like cat
are transducers, where functions like map
return transducers, but it's common to refer to both as transducers.
map
s transducer arity is the same pattern as the (defn my-transducer [search-term] (fn [rf] …))
example right above
Hi! I am using calva in vs code, and was woundering if there is a command reload the current file together with its dependencies and nested dependencies. I can use "Load/evaluate current file" but if I make a change on a deep file, I need to go through reloading each ns in order to successfully run it on target ns. Cheers!
from (doc require)
> Recognized flags: :reload, :reload-all, :verbose
> :reload forces loading of all the identified libs even if they are
> already loaded (has no effect on libspecs using :as-alias)
> :reload-all implies :reload and also forces loading of all libs that the
> identified libs directly or indirectly load via require or use
> (has no effect on libspecs using :as-alias)
(and should be (require <my-ns> :reload-all)
of course.
or just reload the namespaces as you change them which is what i do
Another transducer question: would anyone have an example of how to test a transducer? There's absolutely NOTHING on Google. I'm using clojure.test
. I mean it won't be difficult, but seeing a few realistic examples would be handy
I'm curious what kind of transducer you're creating. It's common that you don't actually need to create a new transducer and can compose the transducer from simpler transducers (eg. map, filter, remove, keep, cat, take, or something from https://github.com/cgrand/xforms etc).
eg:
(defn my-cool-transducer [my-args]
(comp (map (first my-args))
(keep (second my-args))
cat
(partition-by :foo)))
@U7RJTCH6J be my guest https://github.com/BizMentorAI/nace-lookup/blob/master/src/workers/autocomplete/transducer.cljc and being used here https://github.com/BizMentorAI/nace-lookup/blob/master/src/workers/autocomplete/autocomplete.cljs#L19 I wonder what do people think about this. Essentially it was a large thing and I wanted to extract it out and not have it as one large reduce with lambda. Not sure whether extracting it to a fn would be better, either way I wanted an excuse to see what transducers are all about and how can they help me. Anyway, opinions welcome.
I think practically it'll be easier to test as the step function can be used separately, that's a good thing by itself.
A couple things I notice just from skimming:
• I see a combination of map and filter which means you can probably compose your transducer as mentioned above
• The reduce inside the reducing function can probably be removed by composing cat
in the stack
• I see that you're using conj
explicitly as a reducing function inside of your reducing function rather than rf
• One of the transducer rules is to chain the return value:
Eg.
(doseq [l6-item filtered-l6-items]
(xf acc l6-item)))
;; should be something like
(reduce xf acc filtered-l6-items)
> ; TODO: Put persistent here? Will it work in bb/JVM?
Adding a call to persistent in this transducer would introduce unnecessary coupling.
I would probably move that to a separate function, but it's probably unnecessary as you can probably just use into
eg.
(into [] (my-transducer args) input-coll)
Thanks, that's great, I'll have something to work on 🙂
If you want to write a simpler wrapper function for the transformation, that would also make sense: eg.
(defn get-results [search-term coll]
(into [] (my-tranducer search-term) coll))
Yeah the persistent I don't even understand what it is, but I was getting rubbish from clj->js
if I didn't have it, so...
OK. But you say that for the actual body I can replace it by that (comp...)
stack, right? That's that'd handle all the arities?
Here's a rough sketch (hastily written and untested)
(defn my-transducer [search-term]
(let [regexp (re-pattern (str "(?i)\\b" search-term))]
(comp (mapcat :items)
(filter (fn [{:keys [l6-item]}]
(match-l6-item l6-item regexp)))
(map (fn [l6-item]
(assoc l6-item
:l4Item
{:code (:code l4-item) :label (:label l4-item)}))))))
;; usage
(into [] (my-transducer search-term) coll)
Oh wow. That's definitely much more readable.
And shorter!
Thanks Adrian, very much appreciated!
Are you familiar with the ->>
macro? I would usually start there since I feel like it's a little easier to grok than transducers. If that feels comfortable, you can often replace it with a transducer stack where it looks similar to ->>
, but replacing ->>
with comp
and using into
or transduce
.
I know what it does, yes. Haven't used it yet, but I know about it.
That style would look something like:
(->> coll (macat :items) (filter ...) (map ...))
It's really just a convenient form of chaining. In other languages, it would often look like:
coll.mapcat(f).filter(f2).map(f3)
Oh I do prefer the nested syntax and the transducer honestly. I got used to it 🙂
Are "down" migrations important? having to write the inverse operation every time you're restructuring the database takes the joy out of it... isn't it possible to analyze the "up" migration and generate a "down" migration from that so I don't have to write it? (and maybe even do a better job at it than me, avoiding bugs)
Depends on your db update strategy, but typically, ‘down’ migrations are a bad idea in production as often-times they are lossy operations: e.g. you may drop a column that was just added that has been populated with some data. With that being said, I do write ‘down’ migrations, even though I never run them in prod - they are useful for the development phase, and for scenarios where I have to swap between dev branches on a local dev database I use. I write these scripts manually, doesn’t register as something that is large enough to bother automating really. In terms of an overall db deployment strategy, I have found requiring db backwards compatibility to be very helpful. I.e., we require every db version upgrade to not break the previous version of the app. This allows us to divorce the prod db upgrade process from the prod app upgrade process, and makes app rollback easy in cases where prod errors arise.
Pretty much the same here. We have "up" migrations but no "down" and we also make sure that each migration is non-breaking.