Fork me on GitHub
#beginners
<
2023-05-17
>
Can08:05:36

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?

Can08:05:35

Thank you so much , have a good day! 🙂

anovick10:05:26

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?

delaguardo10:05:34

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.

skylize14:05:05

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?

anovick15:05:04

@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?

seancorfield16:05:03

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...

🎯 2
rickheere13:05:25

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?

delaguardo13:05:03

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

delaguardo13:05:02

if your created at runtime keywords will be nether serialized - you're good

rickheere14:05:19

Thank you, very insightful. The keywords need to be serialized because they are part of a xtdb document. I'll consider my options.

delaguardo14:05:47

(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

rickheere19:05:56

Ah, sweet, thank you, that's is helpful.

Jason Bullers18:05:17

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?

pez18:05:42

I think you are a bit out of luck here. Hoping I am wrong.

seancorfield18:05:20

Yeah, as someone who uses Selmer a lot, the "red squigglies" everywhere can be a bit distracting...

seancorfield18:05:18

It is mostly confined to embedded JS code tho', if you're talking about .html pages as Selmer templates.

seancorfield18:05:05

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).

Frank Henard18:05:03

@U04V70XH6 Do you prefer Selmer of Hiccup, or are you forced to use it? Just curious as someone who has only used Hiccup.

seancorfield18:05:01

For HTML pages, our UI/UX folks have to be able to work on them so Hiccup is not an option at all.

👍 2
seancorfield18:05:14

Also, for our HTML emails -- for the same reason.

seancorfield18:05:06

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).

👍 2
seancorfield18:05:46

Nearly all our UI is React.js these days tho' -- as far as our main, customer-facing stuff is concerned.

👍 2
Jason Bullers19:05:30

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?

skylize19:05:36

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

skylize19:05:51

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/

Jason Bullers19:05:13

@U90R0EPHA interesting. I'll let you know if I get a chance to fiddle with it.

seancorfield22:05:33

@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).

Jason Bullers22:05:23

@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:

2
pez08:05:06

Very cool!

Jakub Šťastný20:05:07

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!

phronmophobic20:05:13

The inner function[2] is a reducing function (often abbreviated rf).

🙏 2
2
phronmophobic20:05:31

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.

phronmophobic20:05:41

I think xf and xform are synonymous.

clojure-spin 2
Jakub Šťastný20:05:11

Oh good link. Yeah xf I say transformer (should be noun, although transformer sounds a bit funny, but it's something like that.

👍 2
phronmophobic20:05:01

There's also: > A transducer (sometimes referred to as xform or xf) is a transformation from one reducing function to another:

ghadi20:05:23

[1] should be rf

2
phronmophobic20:05:09

Now I want to start calling transducers transfducers

lread20:05:03

Hey @U024VBA4FD5, if you are ramping up on transducers, I personally found https://youtu.be/r6t6jsNBZDo to be very helpful.

Jakub Šťastný20:05:06

@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!

lread20:05:56

Is that what the f stands for in rf? Yeah, that vid helped to realize they are simpler than I thought.

dpsutton20:05:40

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.

2
Jakub Šťastný20:05:33

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?

ghadi20:05:10

they are both reducing fns

dpsutton20:05:11

“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

Jakub Šťastný20:05:36

@U11BV7MTK OK that's a good quote.

dpsutton20:05:00

keep it front and center while thinking about things. i’ve found it helped quite a bit

phronmophobic21:05:10

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.

dpsutton21:05:23

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

Jakub Šťastný21:05:16

@U7RJTCH6J right, that does make more sense actually.

phronmophobic21:05:47

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.

2
dpsutton21:05:21

maps transducer arity is the same pattern as the (defn my-transducer [search-term] (fn [rf] …)) example right above

Bruno Porto21:05:21

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!

dpsutton21:05:42

(require <the-ns> :reload) should do it

dpsutton21:05:30

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)

dpsutton21:05:38

(and should be (require <my-ns> :reload-all) of course. or just reload the namespaces as you change them which is what i do

👍 2
2
Jakub Šťastný21:05:06

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 clojure-spin

phronmophobic21:05:37

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).

phronmophobic21:05:32

eg:

(defn my-cool-transducer [my-args]
  (comp (map (first my-args))
        (keep (second my-args))
        cat
        (partition-by :foo)))

Jakub Šťastný21:05:08

@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.

Jakub Šťastný21:05:29

I think practically it'll be easier to test as the step function can be used separately, that's a good thing by itself.

phronmophobic21:05:06

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)

🙏 2
phronmophobic21:05:39

> ; 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)

Jakub Šťastný21:05:31

Thanks, that's great, I'll have something to work on 🙂

phronmophobic21:05:07

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))

Jakub Šťastný21:05:07

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...

Jakub Šťastný21:05:56

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?

phronmophobic21:05:52

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)

Jakub Šťastný21:05:08

Oh wow. That's definitely much more readable.

Jakub Šťastný21:05:45

Thanks Adrian, very much appreciated!

phronmophobic21:05:21

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 .

Jakub Šťastný21:05:42

I know what it does, yes. Haven't used it yet, but I know about it.

phronmophobic21:05:04

That style would look something like: (->> coll (macat :items) (filter ...) (map ...))

phronmophobic21:05:58

It's really just a convenient form of chaining. In other languages, it would often look like: coll.mapcat(f).filter(f2).map(f3)

Jakub Šťastný21:05:04

Oh I do prefer the nested syntax and the transducer honestly. I got used to it 🙂

👍 2
anovick21:05:42

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)

DenisMc23:05:45

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.

seancorfield23:05:29

Pretty much the same here. We have "up" migrations but no "down" and we also make sure that each migration is non-breaking.

ghadi23:05:25

Club Roll-forward-only here

2
😆 2