Fork me on GitHub
#off-topic
<
2024-01-03
>
Niki00:01:56

Just had this idea for an enhanced loop where on recur you only specify values that differ from current values. Has this been tried before? Yay or Nay?

👍 5
hiredman00:01:36

you may be interested in a "syntactic monad" (don't ask me about the name, copied iit from the scheme srfi) https://downey.family/p/2024-01-02/synmo.clj.html

Niki01:01:16

I will need help understanding what’s going on

hiredman02:01:14

It is a similar kind of optional rebinding with implicit parameters but applied everywhere

hiredman02:01:52

Not that my clojure impl there is complete

p-himik05:01:59

There's also a bug. :) Can't make the fn named: > Don't know how to create ISeq from: clojure.lang.Symbol Because of that ~@maybe-name.

Alex Shulgin06:01:40

@U050UBKAA looks legit. Why off-topic, though? 😄

Niki14:01:04

@U04CRS4B49L I didn’t found better channel. Which one should it be?

Alex Shulgin14:01:33

Looks like it belongs to the main channel #C03S1KBA2. Don't quote me on that — I'm rather fresh here. 😅 But my understanding is that off-topic is for everything that's not about Clojure, so I found it surprising.

Jonas Östlund15:01:43

I would probably use merge for this:

(loop [{:keys [x y z] :as m} {:x 0 :y 0 :z 0}]
    (let [step #(merge m %)]
      (cond
        (< x 10) (recur (step {:x (inc x)}))
        (< y 10) (recur (step {:x 0 :y (inc y)}))
        (< z 10) (recur (step {:x 0 :y 0 :z (inc z)}))
        :else [x y z])))

Noah Bogart15:01:35

that syntactic monad is cool

Niki16:01:43

Can you explain what it does? I’m drawing blanks here

p-himik16:01:13

tl;dr: it makes it so you don't have to pass prog a b c everywhere - it does it for you. The $ macro handles letfn, fn, and symbols. I won't consider letfn since it's not used in the example. Any fn followed by argv+bodies will be rewritten so that the argv has the prog a b c arguments added at the front. So ($ fn [w] ...) there becomes (fn [prog a b c w] ...). Any symbol, if followed by a vector, becomes that same symbol in the first position in a list (i.e. like a special/macro/function call) with prog a b c and whatever follows being the arguments. That list is wrapped in a let with the bindings specified by the aforementioned vector. Any symbol without a vector gets the same but without extra bindings.

👍 1
p-himik16:01:44

user=> (macroexpand-1 '($ fn [w] (println prog a b c w)))
(fn ([prog a b c w] (println prog a b c w)))

user=> (macroexpand-1 '($ recur [prog (inc prog)]))
(clojure.core/let [prog (inc prog)] (recur prog a b c))

hiredman16:01:02

I mentioned it because it can do almost the same thing with almost the same syntax as loop+ (of course I didn't implement it for loop or let so you have to recur to a function)

Niki17:01:24

@U2FRKM4TW what does ($ vector w) do?

hiredman17:01:31

it is transformed into (vector prog a b c w)

Niki17:01:43

Would hypothetical as macro do the same?

(as [% [prog a b c]]
  ((fn [% w]
     (if (> a prog)
       (let [prog (inc prog)]
         (recur % w))
       (vector % w)))
   1 2 3 4 :foo))

hiredman17:01:44

when a form is prefixed with $ it implicitly adds the prog, a, b, and c arguments to it, and what it means to add the arguments to it depends on what the form is (fn it args them to the arglist, function calls it adds them as arguments in the call)

hiredman17:01:09

yeah, the annoying thing with doing it like as is that the as macro itself now has to walk the code replacing % with what you've given it

hiredman17:01:46

the nice thing about the syntactic monad style is $ just examines the form it is used with directly without have to code walk into child elements

Niki17:01:39

You mean it’s nice for the macro author, not for the users?

hiredman18:01:37

that is a complicated question. I think it can be a simpler macro. The incomplete buggy version there can be intermixed with forms it doesn't understand like let, as long as you don't use it directly on let ($ fn [..] (let [...] ($ f ...)) would work fine, but if as wasn't completely and didn't understand how to code walk let, depending on what semantics you want it to have, it might not work out right

hiredman18:01:59

and being simpler means it is potentially easier to get right, and if it goes wrong and breaks it is potentially easier to understand why

hiredman18:01:27

but among clojure users there does seem to be a strong preference for as style macros, which are complex and require at the very least code walking and in the case of something like(I think maybe as doesn't need to understand what it is walking very much?) the go macro a complete compiler in there

hiredman18:01:39

I think as style macros like the above are slightly more amenable to static analysis. I am not sure if a kondo hook could be written for it as is, but most of your code still looks like normal clojure code

Niki18:01:52

What’s so hard about walking code?

Niki18:01:19

Why is it thing to avoid?

hiredman18:01:26

I think it might not effect the as macro there as much, but depending on what kind of transformations you are doing, you may want to transform forms differently based on their semantics, not just syntax, and macros mean the semantics are open, someone could write a new macro that expands in to a let introducing a binding, but your code walker won't know about it and won't know that the form introduces a binding

hiredman18:01:10

at which point your either need to have your macro expand forms before walking them (which is a whole thing) or it just won't work right

Niki18:01:21

Yeah I feel like when macros have to know the semantics of the form and do special treatment for them that’s the thing to avoid

hiredman18:01:24

it is not uncommon, if you wanted to add the optional rebinding syntax the simple replacement as does it not enough

hiredman18:01:24

even the loop+ as shown in your screenshot would need it to walk down and find recur calls and transform them (you could avoid it by having a recur+ macro)

hiredman18:01:48

actually I take that back, I don't think recur+ would solve that

hiredman18:01:00

there are particular styles of macros that make this sort of thing easier where you use the macroexapander as your code walker called ck-macros http://wiki.call-cc.org/eggref/5/ck-macros

hiredman18:01:16

and you can sort of do that style of thing in clojure https://gist.github.com/hiredman/b2096b04927a9c9db480c39806dac297

hifumi12307:01:08

I remember earlier someone here mentioning they became a CTO at an early startup and were debating whether or not to use Clojure. Well, I am now in a similar position. I am going to be a co-founder for some very, very early startup. I don’t want to give much details publicly, but we are aiming at a SaaS with frontends for web browsers and mobile devices. Unsurprisingly, me -- a person in the Clojurians Slack with a ClojureScript day job -- wants to use Clojure. But we are also very pressed on time for an MVP and I am not sure if it’ll be a good idea to use ClojureScript (or even Clojure at all), because my partner has intentions to hire others to continue development (and I simply manage it). I’ve seen first-hand how tricky it is to hire ClojureScript devs. I’m also vaguely familiar of the DX that TypeScript provides (spoiler: I love the autocomplete, but dislike the large swaths of libraries built with Webpack or CRA in mind). I am quite undecided so far on frontends, and to some extent, even the backend. So I am begging for everyone’s advice 😄

hifumi12307:01:13

To summarize my problems with CLJS frontend: hiring is tough and code re-use is highly debatable when you throw in React Native to the mix. I don’t think ClojureDart is worth using at this time because the hiring problem is exacerbated and I have little experience with the tech. Although I am very comfortable with ClojureScript for web frontends, I don’t want a frontend that is stuck with a bus factor of 1. As for backend: I am much more comfortable sticking with Clojure there. If I don’t use Clojure, it’d be due to ease of deployment (compared to e.g. Go (as long as you avoid cgo)) or lack of convenience (e.g. Reitit’s OpenAPI generator doesnt feel as polished as Go’s swag)

Oliver Marks07:01:28

If you are pressed on time but know clojure seems that a benefit as you can build rapidly if you know the language, as for hiring I think. There are more people looking than hiring. I am looking currently but will likely end up working in python because of the lack of jobs in the clojure world and the large number of python jobs out there.

👍 1
Martynas Maciulevičius08:01:08

Currently I'm trying out this library but I found that I couldn't do on-the-fly updates so I'm trying to write my own version of it that could update the DOM like HTMX's hx-update-oob does: https://github.com/Instawork/hyperview/ So for this one you could do your backend in Clojure and the app would be this Hyperview XML parser thing. And screens would be sent from backend in XML. But maybe it will work for you. It will make a single dev very productive when set up. Also one negative thing is that you need many frontends and currently web frontend is lacking. I was trying to "have all three" but web just is not up to the par with bad URL and so on. (Also looking for a job btw and I'm in EU 😄 )

p-himik08:01:54

DX that TypeScript providesIME that DX, while being great, gets overwhelmed by the amount of work needed to make TS happy, or to make the devs happy in terms of syntax. As an example of the latter, check out https://github.com/bokeh/bokeh/tree/branch-3.4/bokehjs/src/compiler I used to work on Bokeh quite a lot and I was not a fan of the move from JS to TS. The linked dir is just something they had to come up with to reduce all the boilerplate that's needed both due to TS and due to how Bokeh works. Previously, it required much less code and thinking.

hifumi12308:01:07

@U02DXJUS5JA That is a good point. Although I can probably iterate fast with Go, TS, etc., I am probably much faster with Clojure(Script). @U2FRKM4TW Hm, you bring up a good point regarding TypeScript boilerplate. At the end of the day, my ideal stack would let my backend host some Swagger or OpenAPI definition, then a library on the frontend (like martian or a react-query code generator) can consume it. Since it’s pretty late where I am, I will wait for others to chime in and look at responses tomorrow.

Oliver Marks08:01:03

Seems like you have answered your own question, if your starting the project and speed is the main driving factor then go with what allows you to get your MVP quickly.

Oliver Marks08:01:22

You can always rewrite at a later date :)

p-himik08:01:15

As an amusing anecdote, I have a friend that's a JS/TS developer. He says he's pretty happy with it and doesn't feel like learning anything else. However, occasionally he does have problems with it where he calls me and we try to figure out what's going on. His general mood then rapidly shifts from "TS is alright" to " TS, what the *, what is this BS!?". :D And I cannot fathom myself using something like that. No more, I've had my fair share. Similarly, one client of mine uses Swift and I review some parts of the code that interact with a Clojure backend. I myself have a pretty similar disposition towards it as my friend has towards TS when it doesn't do what's needed. It took the Swift guys 50 lines of code to just read a file in a test, because reasons. They had to start a thread inside an async context and immediately join it - again, because reasons, and not good ones. And so on, "it just never ends".

Omar08:01:41

I think do whatever is faster with you with the resources you have. Are you going to be building the entire MVP yourself or will have another with you? If you're able to hire one more, if s/he has produced quality work then just do whatever you both have the most experience in. Several years ago when I was newish at clojure I ended up writing a quick one off project with an extremely strict deadline in rails in a few days. I wouldn't do that now, but that's what let me move the fastest at the time.

👍 1
Patrix11:01:34

I’ve been in a similar situation but jumping in after some MVP was already well under way but in PHP. I figured I’d let it continue that way but the situation just kept getting worse… So eventually I took the reins and am currently halfway through a clojure + HTMX + polylith rewrite and I find myself wishing I had gone this way much earlier. Not so much due to language choice however, that's just what makes me productive and happy. And just so happens Clojure is great for rapid iteration anyway. If you’re the CTO you should have authority to make these decisions. And also the humility to change your mind later and allow a rewrite in something else later lol. Do what works best for getting things off the ground. Plus there’s no shortage of people wanting to work with clojure. if in the process you make the clojure job market a tiny bit better that’s a nice bonus lol (but takes second seat to the business success of course).

Martynas Maciulevičius11:01:33

clojure + HTMX + polylithYes but he wants web AND app. So HTMX is not directly usable. This is why I want to have something similar to HTMX "to rule them all". This way the app would be just another browser.

7tupel11:01:28

Given that I have already built early MVPs using Clojure/Script in a short time frame I would stick to it. Finding more developers to proceed further should in my opinion not be the most pressing manner in this situation. And as stated here before there are many developers with a love for Clojure that cannot use it in their daily job because there are not that many Clojure positions. I'm sure it will be feasible to get some of them interested in working on your product, especially if remote work across timezones is an option for you. And there is also the option to find and recruit some junior developers or students that you could teach the basics of Clojure to join you as part of the team (yes this will take time and you will still need some more experienced Clojure developers). Technology wise I would go with Clojure without a big framework in the backend and react/native on the frontend and try to decouple as much logic as possible for re-use.

👍 1
Oliver Marks11:01:37

Also more likely to get the app finished using the language you enjoy the most so that's worth considering, I don't think the worry about hiring people in the future is an issue if your really stuck you can train people but looking at the current market I don't think finding dev's is difficult

Noah Bogart12:01:44

Our start up was originally written in Python but switched to clojure pretty early in development. Is been clojure for 4.5 years. However, we also use Vue in the frontend and while that’s made hiring for FE devs easier, it’s complicated both the tooling and learning the code base. Clojurescript can still feel janky to me, but cljc is just so dang nice.

👍 1
Noah Bogart12:01:48

If you need an app too, hire an ios dev. Don’t try to use one code base for everything. That way lies pain and frustration for literally everyone

Oliver Marks12:01:05

I think in clojurescript land we tend to jump to react as well, partially because of libraries I guess, though I think I would look more at htmx or domdom how ever if I was building from scratch dependent on what I would be building I would love to see some more native libraries though instead of wrappers 🙂

haris14:01:28

Previous CTO here, bit of a Clojure newbie but spent several plus years working with JS, TS, and React/React Native at a small company with similar limitations/resources as yours. One thing I would recommend is to take whatever length of time is needed and then some to get the foundations right. Any time not spent doing so in the name of speed always bites you back later in my experience; and it bites back hard leaving you with no quick solutions and costly rewrites. "Getting the foundations right" is a very nebulous goal I admit, and very project-dependent. But, one concrete way I can see that being done is keeping all of the state/model code in Clojure/ClojureScript and using plain JS or TS for the frontend code. Model = limited dev talent, but limited code churn, UI code = whatever is quick, anything from npm, lots of development talent, but lots of churn too.

Benjamin C15:01:50

Having built mobile apps both in React-Native via CLJS and Flutter via ClojureDart, my personal vote goes toward ClojureDart. Even without a repl the experience was much nicer. Once it has that, and FlowStorm support, it will be chef_kiss in my personal opinion.

😮 3
p-himik15:01:50

FWIW, the hybrid approach that Haris outlines above is exactly what dnolen promotes here and in some other talk(s): https://www.youtube.com/watch?v=3HxVMGaiZbc

haris16:01:55

Yep, from the SPA-lens Nolen's talk is really nice. Another way to implement separation is the more MPA-route, state/logic on a Clojure backend server, actual frontend has little logic and gets by with any stack/framework. You can also do an in-between the two approaches where the backend still sends JSON but through a REST/Graph/API you control. Then, your frontend just becomes another consumer of your backend logic.

haris16:01:38

General idea is to use really strong tools (Clojure/ClojureScript) where churn can't be tolerated, and be liberal in areas where churn is acceptable.

haris16:01:25

Aside from usual benefits, separation of the tech-stack really helps in the talent-space. Frontend-only developers can still be productive by just using the APIs built, won't even need to know Clojure. For the growing-stars/experts on your team, having a familiar starting point in the frontend and a stable albeit obscure backend helps build a roadmap for growth, just like Nolen's talk mentions.

👍 1
mauricio.szabo18:01:42

So, about ClojureScript: react-native with CLJS was pain for me, but not because of CLJS - more because of RN (we were not using Expo).

👍 1
mauricio.szabo18:01:22

But it was way better than using RN without CLJS, honestly; I also need to add that I firmly believe that my tooling (editor+plug-in) works way better than others regarding CLJS (maybe not so much for CLJ), so that can also explain why I liked the experience overall.

mauricio.szabo18:01:22

The problems I had with RN were related to "native" stuff like the iOS and Android (Java) stuff - these won't change if I decided to move to JS, and they would be worse if I decided to use native, so... 🤷

hifumi12322:01:39

Thanks for the responses, everybody! Many of you pointed out that I may be worrying too much about hiring as opposed to just getting something done. After some thought, I think I will continue with Clojure(Script), due to my experience and relative speed prototyping with this stack. As for the mention of dnolen’s talk, I have seen it many times and love the idea, but using :js-provider :external in shadow-cljs is often one of my “last resort” techniques for consuming JS libraries 😄 though I like the theory behind “frontend logic in CLJS, frontend UI in JS/TS”.

🎉 2
Patrix22:01:22

Re: HTMX. I wasn’t trying to tell you what to use :) only what I’m up to now. However the HMTX people also created “hyperview” which is an XML like HTMX made for mobile apps. https://hyperview.org/

practicalli-johnny23:01:36

Hiring is a challenge and time intensive regardless of technology. Choose a stack that is relevant for the MVP to be delivered, not some other constraint that doesnt have a clear definition. Its far more likely that the whole thing will need to be re-written due to architectural & design changes that were not initially understood. Limiting yourself to choosing only technologies you think you can hire for can compromise the successful delivery of an MVP and the success of the startup.

tlonist11:01:48

For your information, I too had the similar concerns and boldly decided a full stack clojure approach to my startup. Very happy with the decision!

Martynas Maciulevičius11:01:27

> For your information, I too had the similar concerns and boldly decided a full stack clojure approach to my startup. Very happy with the decision! Could you share when this happened and what is the webpage of your startup?

Mateusz Mazurczak18:01:28

I don't think clojurescript will be a problem. I don't know when you were looking at the recruitment part, but 3 years ago and now is a completely different story. The market has changed, there are a lot of people applying, also with clojurescript experience. And from my personal experience using clojure makes you need fewer people, so having just three developers in clojure could be equivalent of 6 people team in another language.

haris18:01:36

Good point. Escaping from the churn and building more maintainable systems for the long-term is what drove me to learn Clojure/ClojureScript in the first place. JS may have gobs of talent and packages, but with it comes the 60/60 rule (ex. 60% of coding time is spent in maintenance).

haris18:01:37

Honestly, JS needs it's own variant of that rule, like the 60/60/60 rule: 60% of your coding maintenance time is spent fixing breaking JS packages.

tlonist23:01:09

@U028ART884X My company was founded last year June, and I started coding in August. We are building a SaaS Management Platform, so it is B2B product. You can view our product introduction from here (https://smply.one it is in Korean). We'll be preparing demo page soon.

👍 1
bsb09:01:23

Anyone have experience with a polyglot policy? ie. Clojure/Script where it makes sense but all devs also need to be able to touch JS (or other alternate), while JS devs need willingness to learn Clojure? It hedges popularity but may hurt productivity...

henrik13:01:11

I’m having trouble searching for this, don’t know exactly what terms to use. Consider this HTML:

<div>
  <span>
</div>
<div>
  </span>
</div>
Also,
<div>
  <strong>
    <em>
  </strong>
  </em>
</div>
What do you call this sh*t? Is it valid HTML (God, I hope not)?

p-himik13:01:27

It is not valid.

6
henrik13:01:25

I suspected as much. I don’t think I’ve seen it in practice since the early 2000's. Hiccup etc. is good in that it’s actually not possible to express things like this. Do you know what it’s called? Like, what is the rule set called that defines this.

p-himik13:01:47

I don't think there's an official name for that specific "construct". Every element has a start tag (sometimes optional, but not in this case), its contents, and an end tag (same thing). The first code block, regardless of how it's indented, is treated as the first </div> designating an end tag without the corresponding start tag, which is a violation. Same thing for the second code block and </strong>.

👍 1
henrik13:01:52

I think the phrase I’m looking for is “nesting rules.”

💡 1
henrik13:01:20

At least that gives me content adjacent to what I want to read on W3C. Anyway, thanks!

respatialized14:01:36

MDN's guides to content categories helped me understand HTML's structure a lot better. https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories

👍 1
phill16:01:54

"Enlive" (https://github.com/cgrand/enlive) wraps 2 alternative Java libraries that interpret that kind of wrong-HTML loosely, as web browsers do. I think they are tagsoup and jsoup. Using Enlive you can read the stuff as a clojure.xml kind of tree of elements-as-nested-maps

henrik17:01:26

Thanks! That’s great for absorbing messy (real-world) data. In this case, I was just out to scratch a brain-itch, specifically the name of the rule that this violates.

phill17:01:49

Well, I would go farther than "not valid". It is "not well-formed".

p-himik17:01:37

What's the difference?

respatialized17:01:20

"well-formed": syntax "not valid": semantics

p-himik17:01:16

It's still not valid then. AFAIK there are no clear semantics defined for malformed HTML, so a user client can do whatever it wants. Can even crash.

p-himik17:01:06

Oh, I see - you mean that "malformed" is stronger than "invalid". :) Yeah, I agree.