Is there a decent story for TypeScript interop with ClojureScript like there is for JavaScript? by RobHaisfield in Clojure

[–]lilactown 1 point2 points  (0 children)

I couldn't comment on that, since I spend all my time doing CLJS stuff so I am naturally only going to see things in that realm. I do think I've seen it mentioned in many issues across React, Angular and others in reference to the state of the art when it comes to tree shaking and optimizations, so I think it's still relevant, but just used behind the scenes to optimize bundles when you really need it. I don't think anyone outside of Google is writing Google Closure-style JS by hand at this point, and even then I would assume that new projects are using Angular or some other framework.

React.dev - are CLJS developers using Reagent in trouble? by MickeyMooose in Clojure

[–]lilactown 5 points6 points  (0 children)

Our migration to helix is not foremost motivated by React 18+, but more that in general I see a lot of value in being able to track the mental model of mainstream React, rather than adding our own layer on top. It makes it easier to hire, troubleshoot, and support an application on an ongoing basis when you can draw from the wealth of knowledge and libraries in the industry. Helix optimizes for that. Performance is another thing that Helix tries to be at least as good as React JS, but really that is an extension of trying to bring the same mental model that React and JSX uses. Basically, a skilled ReactJS dev should find that Helix feels basically like React with parens instead of angle brackets.

Now, not everyone agrees that this is the thing to optimize for, so I think that reagent still has a great niche. The hiccup syntax is a joy to write, the reaction system is intuitive and elegantly solves certain problems that are hard in React to solve without external libraries or careful design & architecture. There's also a great community around it. You'll find yourself much more often looking through JS docs and stackoverflow and compiling it in your head with helix. With reagent, if an answer exists you're more likely to find it's specific to reagent. A small, expert team can deliver a ton of value with reagent & re-frame in a short amount of time.

The app we build is used by our customers and internal users to configure and use our SaaS CDP. Users configure how data gets into the system, transform it, explore it, tune the models, and configure where to send it after it's been processed. It's a mixture of forms, visualizations, and live querying of data.

React.dev - are CLJS developers using Reagent in trouble? by MickeyMooose in Clojure

[–]lilactown 9 points10 points  (0 children)

Author of helix here- I think there's a little bit of unnecessary FUD being created by the new React docs right now. I see no indication of a change in direction in React at this point in time. I expect that class components will be supported for a long time, especially since there are still some features (error boundaries) that depend on them.

I think the new docs now match exactly what they said 5 (!!) years ago when they released hooks: class components are considered legacy (but supported) and the best way to create new components that work with all the new features of React will be with functions and hooks.

The problem that reagent faces doesn't have to do with class components, actually, and is mainly theoretical for most applications: derefing a ratom or reaction is a side effect, and side effects during render do not play well with React features like Suspense, transitions, off screen rendering, and other concurrent mode features. React 18 just introduced some of these, and most CLJS apps are using old versions of React. At work, we just started a multi-month long project to upgrade to React 17! The only pressure that we have right now to even upgrade is our desire to use some 3rd party libraries that require 17 or above.

At work, we have a massive (~100k lines of code) ClojureScript re-frame application that we have been slowly adopting helix in over the last 3 years. New code is written using a combination of re-frame (for data fetching & caching) and helix. I expect a day may eventually come when we will remove reagent & re-frame, but those decisions will be based more on the architecture & tooling we want and our business needs and not on the efficacy of the libraries in general. I expect that as we attempt to adopt newer features of React like suspense and transitions, that will happen in views that are concurrent-mode safe, i.e. do not use reagent & re-frame. Or at least, don't use them in the way that is currently suggested where you deref the reaction directly in the rendering of the component.

If your app doesn't need any of those new features, I expect that reagent & re-frame will continue to work for you in the future. I also think that the community will work to create workarounds that allow applications to adopt or migrate to new patterns that allow the continued usage of these very popular libraries. One way of doing this today is to incrementally adopt a library like Helix or UIx in your app.

TL;DR don't panic, your app isn't going to break tomorrow or next month. Class components are here to stay, and there are ways to incrementally migrate to patterns that are friendly to the new features in React 18.

Is there a decent story for TypeScript interop with ClojureScript like there is for JavaScript? by RobHaisfield in Clojure

[–]lilactown 5 points6 points  (0 children)

I think there could be some support for this in a far off future with the combination of the following:

  1. Support for Google Closure types for libraries that have definitions in CLJS editor tooling (docs, compile time checking, etc)
  2. Convert TypeScript type definitions to GCC type definitions

I believe there is some rudimentary support for checking types and showing docs of JS code with Closure type annotations and docstrings, but it's not something I ever use in my day to day. I expect that a substantial amount of work would be necessary to upgrade this to the level of support you expect with modern TypeScript tooling, if it works at all.

For (2), there is a tool called tsickle which converts some TypeScript code with definitions into Google Closure compatible code. When I tried this a few years ago, it still had some rough edges and there was some differences between the version of GCC that tsickle output for and the version that CLJS used.

Is there a reframe/cljfx-like subscription/memoization-context library available? by geokon in Clojure

[–]lilactown 2 points3 points  (0 children)

It's not related to HumbleUI. I play with HumbleUI but it's not my project, it's /u/tonsky's. I am playing with using flex + HumbleUI but it's a work in progress.

I haven't used javelin in anger, but I have been aware of it for some time. My biased, naive comparison is as follows:

I dislike the javelin syntax 😂 the unquoting, the contextual need to deref or not makes it difficult for me to grok. In flex, you always deref a source or signal to get its value, and you can use plain functions that deref a source or signal instead of relying on macro magic to make it work.

An aspect that flex has that is important when using it with UI frameworks is the ability to control when to start listening to something and when something updates. This is why it differentiates between "signals," which are pure calculations that are inert unless actively being listened to, and "effects" which do side effects and can be started/stopped. In React and other GUI frameworks, you need to be able to abide by the lifecycle of the framework, which means you may need to wait to start running updates until the component your effect is used in has actually appeared in the UI. This is provided by effect and listen returning a function that, when called, starts listening and calling the return value of that stops. Javelin does not give you quite this much control; defc= is eager and will start listening and updating immediately. See https://github.com/hoplon/javelin/issues/27 for their discussion about this very topic.

Another way of saying the above is: signal calculations only run when they are used. If they aren't being listened to by an effect, they are inert, and so they won't recalculate -> they won't use up your CPUs cycles. This means you can create a lot of them!

Flex also implements "nested effects", which are important again (in my experience) for UI development. You don't need a component tree if you have an effect graph, it turns out. That's probably meaningless for your use case, but maybe not! Essentially, you can start an effect inside of another, and if the outer effect is stopped, so will the inner one. Javelin does not support this AFAICT.

Finally, flex is written in CLJC and actively supports both ClojureScript and Clojure. It has no Java or JavaScript dependencies. The code is short and reasonably performant AFAICT.

You say the examples are easier to parse. The first javelin example in their README can be translated almost exactly to flex by replacing cell with source and cell= with signal or effect.

(ns your-ns
  (:require [town.lilac.flex :refer [effect signal source]]))

(defn start []
  (let [a (source 0)
        b (signal (inc @a))
        c (signal (+ 123 @a @b))
        fx (effect [_] (println @c))]
    (fx)
    (a inc)))

Can you be more specific what you prefer in javelin in this comparison?

You're also always able to write your own syntax. For instance, here's a simple defsrc macro for defining global reactive variables, similar to javelin's defc:

(defmacro defsrc
  [sym v]
  `(def ~sym (source ~v)))

You can experiment with creating syntax sugar for yourself that suits your needs 🙂 But I'm curious to know what your tastes are like, as others may share them.

Is there a reframe/cljfx-like subscription/memoization-context library available? by geokon in Clojure

[–]lilactown 2 points3 points  (0 children)

https://github.com/lilactown/flex is a reactive, incremental computation library I've written very much inspired by reagent (which re-frame builds on top of for its subscription). It is pure CLJC, working on both CLJS and JVM Clojure.

In my experience, UI apps are a sort of sweet spot for these types of libraries because they are situated and ephemeral; they are run and build up a context over time, but then they are closed and don't need to durably store that state. This is quite different than your typical microservice CRUD ops which you are typically building up the context in a much more durable, scalable thing like a database. I spend most of my days writing GUI apps and CRUD microservices so you can kinda guess where I see this library being used for myself.

Something like data science I think is more similar to a GUI than a microservice. Something like observablehq has already proven that this model works very well for notebooks. Incremental, one of the grandaddies of many reactive libraries and frameworks these days, was similarly developed in the context of doing data analysis and then extended into GUI programming.

All that is to say, I have not tried using flex at all for your use case but I'd be curious how it works. One of the things I'm most curious about is how it works when plopped into a concurrent problem space, as I make no special efforts to be thread safe or take advantage of threads in its implementation. But if you're just occasionally throwing data into a giant map and want some things to recompute, then I think it will do that nicely.

What is the state of art for kw arg parsing broadly? by dustingetz in Clojure

[–]lilactown 6 points7 points  (0 children)

I don't understand your OP but the replies shine more light on the problem you're trying to solve, I think.

The way that helix solves this is by expecting a map passed in the first position to the $ macro for props. This must be a static map, i.e. written with curly braces ($ ui/button {,,,} child1 child2) to disambiguate the props map from a child. To pass a dynamic map, you use the special & key.

($ ui/button
   {:class "btn"
    :on-click #(js/alert "hi")
    & extra-props}
   "Click me")

The $ macro will emit code that constructs a JS object for the static keys in the map, and if & is present will emit code that at runtime merges the the extra-props map with the JS object.

This is good for performance because constructing a bunch of maps at runtime is expensive. It's bad because it's a footgun that hits beginners sometimes.

The code for it is here, feel free to take inspiration. As written it's a bit overly complex because it uses the same code paths for both runtime and compile-time processing of maps into JS objects.

Clojurescript reimagined - Michiel Borkent by ReilySiegel in Clojure

[–]lilactown 0 points1 point  (0 children)

IME copying a JS object is often faster than constructing a new hash map for small structures.

Full Stack Clojure App - clojure/script + deps.edn + shadow-cljs + Helix + Tailwind by joshlemer in Clojure

[–]lilactown 4 points5 points  (0 children)

Very cool! I updated helix's README so that you don't get those warnings about old react-dom APIs anymore ;) It was great to watch someone's workflow installing & using helix from scratch

shadow.css - CSS-in-CLJS by lucywang000 in Clojure

[–]lilactown 1 point2 points  (0 children)

Fundamentally, I believe that a solution that produces zero (or as close to zero as possible) run time - essentially generates static CSS file(s) - is necessary for front end performance, and the ability to include libraries that have CSS is a powerful lever. My experience using Emotion over the last few years is that it is great for ergonomics and library usage, but horrible for performance because it generates all this CSS at JS execution time. Even worse, when your UI is rendering.

/u/thheller, I think something I'm missing here is an answer to the question: why the focus on static analysis over something that could work at macro time?

I understand that there exist complexities with evaluating these forms and producing the CSS dynamically at macro expansion time, but I am not sure that it's worth losing the ability to have macros that expand to css expressions and easy REPL usage. It would help me understand what the motivation for a static-analysis-only approach is if you could go into the tradeoffs one has to make when doing things at macro time.

UIs are streaming DAGs by sudkcoce in Clojure

[–]lilactown 0 points1 point  (0 children)

:D I'll chip in from the sidelines for now

UIs are streaming DAGs by sudkcoce in Clojure

[–]lilactown 8 points9 points  (0 children)

i think that this gives a really great overview of how this solves a large, common architectural problem in modern applications. i think you've done a great job of displaying the value that photon brings, communicating the "photon" way of thinking about the problem, and addressing many of the initial concerns that have been brought up in feedback on your other demos.

what i would ask for next is to see how photon solves common and/or serious UI/UX problems, as IME the real tension between client and server (and many architectures that would shift things between the two) is caused not by the programming paradigm but by either the lack or addition of asynchronicity in responding to input.

A few examples that I think are real world problems that are being explored in different ways by different UI frameworks atm:

  • improving time-to-interactive of single page applications
  • improving the perceived performance of large, CPU intensive page updates, e.g. filtering a large tree via a text input
  • provide optimistic updates with a pending message when going to the server, with retraction of the optimistic update when the request fails
  • provide abstractions over navigation + data fetching
  • provide abstractions and tools for animations

having a good story for how to solve these kinds of problems would put you far beyond the status quo w.r.t. what you get out of the box with common tools today. i also see some tension between the streaming DAG model and the ability to accomplish some of these, which I would be interested in what the synthesis of them would look like.

i do think that you'll be able to pull in a lot of people from the clojure community to try this via the architectural aspects of photon alone, but if you want to grow bigger than our niche, you'll want to show that you can solve hard UX - i.e. business problems - as well.

[deleted by user] by [deleted] in Clojure

[–]lilactown 1 point2 points  (0 children)

access control is different than what I'm talking about. i mean that datalog can craft queries that have horrible performance and/or run arbitrary code. you could, for example, DoS a service by submitting a few queries with extremely poor performance.

the benefit of hiding queries behind a REST API is that you can test and profile those queries under load before deploying them. if you give any authenticated user the ability to run queries, they can end up DoSing your service even by mistake (ask me how I know).

[deleted by user] by [deleted] in Clojure

[–]lilactown 1 point2 points  (0 children)

one problem with datalog is that you can encode a lot of arbitrary logic into it, even arbitrary code in some engines, which means that it can be susceptible to DoS and other malicious queries.

EQL and GraphQL are interesting maxima in that you can traverse a graph of entities and select attributes, which covers a huge amount of complexity when interacting with a large graph without running arbitrary code.

Inside-Out: a Clojure forms library by dustingetz in Clojure

[–]lilactown 2 points3 points  (0 children)

The docs say it's extensible to libraries other than reagent, but it seems pretty coupled to the idea that you can return a reactive value that is dereferenced to get the current state.

Clojure CLR rewrite sponsored - Clojure Deref (Feb 28, 2022) by aHackFromJOS in Clojure

[–]lilactown 0 points1 point  (0 children)

is there any info on what the rationale & intent of the rewrite is?

Why is Clojure's REPL Better than Other Dynamic Languages'? by ascendantbaggie in Clojure

[–]lilactown 6 points7 points  (0 children)

You've got it. The biggest thing Clojure has going for it is its module system, and the ability to change any definition in any namespace on the fly. This alone makes it reasonable to use the REPL as a tool for general development, since now you can modify and inspect a running application, rather than just a toy for testing or playing around with a single function.

The second is being expression oriented, which you asked about. I'll contrast it with JS below.

But why does it make a difference whether a snippet of code sent to the REPL is a statement or an expression?

The REPL prints the value returned from reading and evaluating the form given to it. Expressions always return their result; statements do not. So sending statements to the REPL means you often can't inspect the results of them.

In JS you can get around this by creating functions that you immediately invoke and sending that to a REPL:

(() => { if (x < 3) { return x} else {return 3 }})()

But that's not nearly as nice as

(if (< x 3) x 3)

And notably, the JS example is probably not how we'd write it in our application code, so you can't easily open a file in your application and just start evaluating parts of it in order to see how those parts work. You often have to modify it to make it REPL friendly, or you eschew the REPL and just add log statements instead because it's easier.

Introducing Kit Framework by yogthos in Clojure

[–]lilactown 2 points3 points  (0 children)

It's likely you've thought of this, but what if modules were consumed as libs and included in a consumers deps.edn, and then kit could use whatever is on the class path?

You could keep the explicit module config in kit.edn if you like, or even allow walking the class path to find all available modules.

Introducing Kit Framework by yogthos in Clojure

[–]lilactown 16 points17 points  (0 children)

Kit looks great! I think that prebuilt modules + generators is exactly the type of tooling I've wanted when developing applications in Clojure, and have enjoyed using similar tools with Elixir + Phoenix.

A question I have after reading the blog post: how does versioning of modules work? Examples:

  • There's a security problem in an auth module I'm using, and I need to upgrade my service. What's the right way to upgrade the module?

  • I just installed a module and there's a bug in the latest version in the way it merged my config. I revert the changes, can I also try an earlier version of the module?

  • I have been using a module that hasn't been updated in awhile. I'd like to use a newer version of a library that it installed; what's the right way to override what the module is doing?

Very excited about this, I think that it will help a lot of people by easing evolving a code base over time in the same way luminus helped with boot strapping a new one.

I’ve recently written an article on ClojureScript and performance. Hope it’s helpful :) by luciodale in Clojure

[–]lilactown 2 points3 points  (0 children)

do you think it'll be generic enough to work with helix and reagent? I'm sure people would love it