React, but in Python(github.com) |
React, but in Python(github.com) |
On first read, I thought by "generating html from function calls" the author was referring the react-style way of building a UI by writing a reactive function that returns HTML.
In this case, I think it's also clear that most things you'd call an "application" abhor this behavior, which is why heavy clients are so popular, and small shops without frontend engineering talent are making a big push away from heavy clients which raises all the hubbub about HTMX etc
No they literally meant generating html like h1() rather than <h1></h1> in something like jsx templates
(I initially wasn't sure if they were talking about the model of server side rendering with client side events passed back to the server through a websocket either though)
lol, no he doesn't
you could still have your html in a template file and parse and return the contents of that.
Their reasoning: https://firefox-source-docs.mozilla.org/devtools/frontend/re...
As an example, here's the Editor main component (same "latest" commit you pointed to):
https://github.com/mozilla/gecko-dev/blob/aec3a901e6f6b3041b...
The codebase is a mish-mash of very old-style React+Redux code, with newer files and usages.
(I know this because I work at Replay.io, which started as a fork of the FF DevTools source, and we spent all of last year modernizing our fork :) Migrated the entire codebase to TS, ripped out all remaining uses of the ancient `React.DOM` helper functions and converted those to JSX, modernized the 2015-era Redux logic, etc: https://github.com/replayio/devtools/pulls?q=is%3Apr+sort%3A... )
Javascript/Typescript is way faster than Python, is ubiquitous and can run pretty much everywhere, has many engine implementations, has an incredibly wide ecosystem, has a type system (Typescript) that blows any Python type system out of the water, runs in browsers, has non-stupid package management systems (PIP is a joke), is easy to get started, etc.
<The world if data scientists/ML/CV people used Typescript.JPEG>
But anyway, the whole language specific package manager business is starting to annoy me. I would like to be able to simply use something like GNU Guix and sometimes I am able to do just that.
Depends on the use case. For ML, sure, it doesn't. For web servers, it depends. For stuff like this React-like tool, it matters.
Poetry is good exactly because it copied the concepts from NPM / Yarn. It's almost a port of NPM to the Python ecosystem, 8 years later.
TypeScript requires you to compile, the whole point of Python for ML and data science is you're running in an interpreted environment where you delegate as much code out as you can for both data processing and algorithms to invisible C++.
The interpreted part is key as ML is about experimentation. It's not like there's any overlap between current web developers and ML people anyway to need an unified bridge.
I guess you have completely missed people running language models on WebGPU then.
I think webasm + webgpu will be a target for a lot of new ml libraries.
I think the key difference is that it’s hard to setup good governance for JavaScript. You’re going to need a fascist linter that is actually enforced, and you’re going to need a tight grip on your developers to force them to really, really, think about their dependencies, but once you have that it’s quite honestly the best language I can think off. The ability to use isolated functions instead of putting them into “classes” is just such a great way to do a solid mix of functional OOP programming, which is obviously heresy to hardliners of either, but it’s just so magical when you do it right.
I think Python is getting there, it was such a great language for such a long time that it sort of forgot to improve. But now it has copied the NPM/Yarn package handler, and hopefully it’ll soon be possible to actually do a Typescript sort of Python, so maybe it’ll be able to win me back. Or to be fair, I think it’s a great language until you have to work together. It’s just so hard to get the codebase governance up with Python that it only really becomes worth it in ML shops where your developers want to work with Python. I’m not sure how Instagram managed, and there are certainly the projects that fit into the Django box which absolutely should be put into the Django box, but the only general purpose language to me personally is currently JavaScript.
Part of that is because we need initiatives like this one. We need “React” in Python or Rust or whatever if we want small dev teams in non-tech enterprise to be able to work with other languages than Typescript. Yes I know we have some C++, and a little Rust, but unlike the rest of our many different projects I’m the only one who can maintain them. Which is actually the primary reason we work with JavaScript, because if we don’t, then the React developer won’t ever be able to go on a vacation. :p It helps that JavaScript has become such a great language, and it likely has exactly because the React dev wants to go on vacation in a trillion IT departments. But I’m all for Python having this React Python so ML heavy shops don’t need that React dev.
But to say JavaScript is atrocious is sort of silly to me and I’m not sure you would have that opinion if you gave it a real chance.
Is that fixed by Typescript, or does it inherit all those kinds of weird JavaScript behavior?
What other languages, besides Python, does this builtin list comparison work in? What's the result when the comparison is `[1, 2] < ["10", "2"]`?
But of course yes, The ML stuff should be Wasm and WebGPU. The point is you can access it from Typescript. Just like most data science libraries are not Python, they just have Python bindings.
tsc is the only compiler I've had to step through with a debugger multiple times.
No DS/ML researcher wants to deal with VM args just to use more than 1GB of memory. That alone would cause so much frustration.
Not to mention unpredictable generational GC.
Or the crazy crap people do with the type system (what you call better other people call a mess).
At least Python has some semblance of runtime type safety.
I'll discuss facts
> tsc is the only compiler I've had to step through with a debugger multiple times.
99.9% of Typescript developers never ever had to do that. Sounds like a "you" problem
> No DS/ML researcher wants to deal with VM args just to use more than 1GB of memory. That alone would cause so much frustration.
You're talking about NodeJS, which is just one of the many JS engines. It's also 200% easier to start NodeJS with a flag to increase the (sane) default memory limit, as compared to the insanity of setting up a Python environment.
> Not to mention unpredictable generational GC.
Is Python GC better? Really? The good thing with Python is that the whole language is so slow that GC is just a drop in the bucket. On the other hand, Millions (Billions?) have been spent optimizing JS engines and it shows. Also: GIL.
> Or the crazy crap people do with the type system (what you call better other people call a mess).
I don't know what you're talking about, TypeScript go Brrr and I get magnificent Intellisense and subtle type checking, while MyPy and friends keeps crapping their pants
> At least Python has some semblance of runtime type safety.
No. And critically it has no semblance of comp time type safety either.
Excuse me what
React builds interactive UIs (web, mobile). After each interaction, the virtual DOM is programmatically recreated and reconciled to the actual DOM.
But reactpy is running on a backend sever??? Is each interaction resulting in a server call?
Can someone explain what is going on?
EDIT: Ah, okay, thanks. Every re-render is a network call. If people complained about web UI performance before..... :)
Similarly to this library, it gives you a `@component` decorator that allows you to create components out of functions. But it also: 1. Includes all existing React hooks (use_state, use_memo, etc) -- so you don't have to learn new patterns. I believe this results in a bit less magic (and so easier debugging) than just using raw variables. 2. Works with ipywidgets, so many existing data apps can be ported over very easily -- Jupyter users celebrate.
I'm not associated with the project, but I know the maintainers (creators of Volia [2]) and they are honestly excellent. I haven't use the project in production, but the getting starting guide is pretty compelling.
[1] https://github.com/widgetti/reacton [2] https://github.com/voila-dashboards/voila
The follow up of that is Solara: "NextJS, but in Python" :)
Now you (and the OP) have me pondering about building UIs in notebooks…
Not sure doing something in React is better than just learning the standard tools, but it's a good option to know exists.
A brief summary of what I did:
* Python-powered apps, like this
* React was used to handle lifecycle. Basically, I avoided writing any kind of lifecycle management because I just wanted to do exactly what React would do. This was achieved via events firing when React lifecycle events fired. If props changed on the server, I'd send the new props to React to see if it would trigger a lifecycle event. If a lifecycle event was triggered, I'd replicate it onto the server which could again render new props.
* The result was a nice seamless support for both native React components and the newfangled Python components (which could be made up of either more React components or Python components).
* The purpose of all of this was to cater to ML engineers who didn't particularly like JavaScript and just wanted to build their analyses with Python and share them with coworkers. We had an automatic deployment system for them to share these.
* Only caveats were around session management. Much of the state was handled on the server via a websocket, so if your connection dropped, you lost all ability to update the layout. Load balancing was going to be tricky, since the sessions would have to be shared somehow. I was thinking about serializing and deserializing using Pickle but never really got around to deployment.
See also: [Dash](https://github.com/plotly/dash)
From the README: Write your Python interfaces in a declarative manner with plain render functions, component classes or even single-file components using Vue-like syntax, but with Python!
- Reactivity (made possible by leveraging observ)
- Function components
- Class components with local state and life-cycle methods/hooks
- Single-file components with Vue-like syntax (.cgx files)
- Custom renderers
Currently there are two renderers:PysideRenderer: for rendering PySide6 applications PygfxRenderer: for rendering 3D graphic scenes with Pygfx
It is possible to create a custom Renderer using the Renderer interface, to render to other UI frameworks, for instance wxPython, or even the browser DOM.
Flavour is a fast, batteries-included, type-safe framework for making modern SPA web apps in Java (and other JVM languages).
* Great Lighthouse scores? Check!
* Real SPAs without sluggish network round trips? Check!
* Modern path-based routing? Check!
* Components (built-in and user-defined)? Check!
* Effortless service calls? Check!
* Refactor frontend and backend simultaneously using your current IDE? Check!
If this sounds interesting, check out these other resources:
* Article in Java Magazine: https://blogs.oracle.com/javamagazine/post/java-in-the-brows...
* 100% Flavour 5-letter word game: https://frequal.com/wordii
* Flavour podcast (created with Castini, a Flavour app): https://castini.frequal.com/cast/show/Flavourcast/f7e171e8-2...
pyjs, streamlit, brython, pyodide, pywebio, gleam, dash, bokeh, gradio, pglet, idom, anvil, pynecone, onu
The last two are YC-funded.
Edit: add https://flet.dev which has superseded pglet
I do not have the answer to your question, parent, but I feel the need to evangelize vanilla JS/ECMA.
You can do everything you could do with jquery and react with very simple constructs now. Web components are slightly awkward, but damn! Possible, and easier than it ever was before js components.
Introducing any additional technologies or node is no longer necessary. The frontend has evolved.
Now. You still have to learn ECMA, but that’s what browsers definitively run as of my writing.
I can pray to mdn for wasm to be a go-to every day tech, but that day is not today.
(Not complete. Pull requests welcome.)
(this is a joke, it was my immediate thought when seeing this. More seriously, probably not for me but looks interesting technically, I'll need to check out how it works)
There was also ComponentKit, which was used heavily in fbobjc: https://github.com/facebook/componentkit
I've been working on a similar thing in Ruby. https://github.com/mayu-live/framework
I find it so ironic that js, a language that runs in the browser, is now used on a server, to rebuild code that is sent back to browser.
https://github.com/pyxl4/pyxl4
example:
# coding: pyxl
from some_module import x_user_badge
user = User.get(some_user_id)
content = <div>Any arbitrary content...</div>
print <user_badge user="{user}">{content}</user_badge>
A huge draw for React is that it _extends_ the JavaScript Syntax. If you still had to compose your React components with a bunch of nested `React.createElement('Component')` calls (instead of `<Component />`) it wouldn't be nearly as useful.
For those types of apps, this 'backend-driven reactive apps' paradigm could be more efficient, because programmers don't have the option to build stuff like an inefficient network protocol or bloated frontend stack.
You'd have to conscious of this.
It's defiantly not a "write a usual React app, but in Python"...every dropdown, menu, modal, etc interaction interaction is a backend call.
I understand the benefits, then again if interacting with these elements requires calling the backend then the cost is quite high.
Making a 'server call' on each interaction is also what web apps used to do before SPAs were a thing. And in many cases it's what SPAs do as well.
Of course, it depends what counts as an 'interaction', but that's been the case since JavaScript existed.
One of the reasons behind the popularity around webapps was that they would no longer need to make 'a server call' on each interaction.
> And in many cases it's what SPAs do as well.
I feel you're grossly misrepresenting what SPAs do. SPAs do calls to send and receive data, not to fetch server-side rendered content.
Then there's the fact that your backend framework has to define the subset of all frontend features it supports in Python. The moment you hit on a usecase that isn't supported in Python, you have to learn JS anyway and write code in the frontend. Except it's the worst kind of frontend code: hacks around broken or missing frontend features that depend on implementation details specific to the framework that are subject to change at any time.
I feel that these kinds of frameworks are better suited towards researchers or backend devs who really do not want to have to set up an NPM project just to have a simple (and I really mean simple) frontend UI to interact with. But don't dare try to write the next gigantic ML app on top of it or you'll have to deal with the performance and maintenance issues that follow.
Blazor was the pioneer here.
Poetry itself said Composer and Cargo were the main inspirations[1]. Both of them work differently from NPM; Poetry has mostly the same differences that its claim checks out. You would be in quite some surprises down the road if the sperficial similarities persuade you to apply one tool’s paradigm to another.
println!("{}", vec![1, 2] < vec![10, 2]); println!("{}", (1, 2) < (10, 2));
Comparing strings and bits is always going to be weird, so I’ve not tested it.
But some frameworks may open the prefilled menu when you click it, and sync the state to the backend, instead of waiting for the backend to “approve” the request to open the menu.
Hypothetical example:
m = menu(1, 2, 3)
if m.opened:
print(“It was opened.”)
Here there may not be any UI latency when opening the menu. The menu opens when you click it, and the state is synced to the backend. However, m = menu(1, 2, 3)
b = button(“open the menu”)
if b.clicked:
m.opened = True
In this program, there will be a round trip to the backend when you click the button to indirectly open the menu.Overall, it depends on how you design the framework and how you divide interaction control between backend and frontend. Controlling every little interaction thru the backend is probably not a good idea, and you should bake some frontend interactions into the UI components.
As for static types to be honest I don't see how it's useful in the context of ML. You might use a Pandas dataframe, which already comes with types enforced inside of it. You shouldn't ever use a for loop on a Pandas dataframe or whatever because then you're running Python instead of C++, Pandas has inbuilt functions and operators.
However Python does have type hints but probably not strict enough, Mojo may improve in this if it really supports both AOT and JIT.
Package management in Python isn't that bad with requirements.txt. The real problem is Python versions are breaking and whatnot (very often ML libraries are months behind latest) but the main Python installation you have only supports virtual environments for packages. Really it should support something like conda out of the box where you create an environment with a Python version.
But it’s not too terrible, it’s it’s not Node or Cargo, but it is really hard to build governance around.
function handler(a: A, b: B) {}
With a suitable tooling, you can generate the type for frontend to call handler(a, b) in a type-safe way, which could be validated on the backend and frontend.
So you're just a "generation" step away from bluring BE/FE communication.
Is it a random third party package (someone just grabbing that name) or who manages it? As it is, it looks like not part of pip. (But maybe you were merely posting it as an alternative?) I've never heard of it before. I have been using various tools already, including merely pip, pipenv, poetry. Do I need to look for the newest tool every month? It begins to feel like the JS ecosystem.
For database connections, large datasets, etc (stuff you can't send off to the browser) use redis.
This is a trade-off between DX and UX. Holding session state means you need sticky session routing, and restarting servers kills your user's sessions. Plus imposing a websocket on your users is a cardinal sin, makes scaling incredibly hard, makes page load times abysmal, makes disconnects a nightmare for both you and your user.
Its fine if you're creating something like stable diffusion web ui though, which is meant to be a single user app.
JSON is an alright storage format but it can’t do infinity, nan, or many other values important to Python. Also, you would have to write a reifier and serializer for every value. These simply create a problem rather than solving one.
Not optimizing DX would be a mistake when the whole purpose of the project is to improve the DX by allowing the users to not write JS/React. Once the DX starts becoming that cumbersome you might as well just avoid it by switching back to native React.
We think option 4 looks the most appealing.
Ironically, many modern SPAs are significantly slower than the traditional apps they replaced. Try using Twitter's webapp on a non-premium phone, for example.
Sometimes it's regrettable that the webdev truck has no rear-view mirror.
Rerendering and recomputing too much (ever encountered these chains of map() and filter() in render()?), many API calls, huge icon sets, huge custom fonts, CSS frameworks and JS frameworks, huge dependency trees with many instance of the same lib running sometime in several versions, big bundles... This heaviness in the name of convenience and branding is not free, it forces people to ditch perfectly fine hardware, which has a high environmental cost... not directly paid by people funding the code).
One of my pet peeves is managed text inputs, where render() is called each time you type a character, for a whole component tree if you are not careful and you happen to pass the content to some parent for some reason. Typing a message in Mattermost on the PinePhone is painful for this reason, there's just no reason typing should be slow even on slow hardware but managed component is considered good practice.
Making people buy newer / powerful and making people wait and using too much energy should be considered bad practice.
Bullshit. All you need to track a user is data you send as part of a HTTP request. Pushing metrics is a fire-and-forget HTTP request away.
The internet feels slower because we're using way more of it, not only in the increased complexity of webapps to improve user experience and implement features but also in the volume of data we're transferring around.
> running A/B tests
A/B tests just means settings/feature flags and metrics. Feature flags is used in way more things than behavioral studies.
> include recording user sessions
User sessions are recorded since ever with zero performance penalty. The very same HackerNews page you're now browsing is tracking your user session whenever you login. That's not it.
> Back "before SPAs" we just weren't doing as much crap in the browser.
Right, and life sucked back then. Why do you think Flash was so popular?
It's trendy to shit on the status quo but it also is low-effort and lacks any insightfulness.
All software becoming slower is a already a meme. This isn't exactly a SPA issue.
> Sometimes it's regrettable that the webdev truck has no rear-view mirror.
The "software is getting slower" meme was the Hallmark of Java in the server when it was released in the 90s. This is nothing new, or specific to web dev.
Also, I feel you're grossly misrepresenting the problem. Reddit's mobile page is considerably slower than the old reddit page, but it's perceived performance is quite good. All posts are cached and instead of full page reloads it just switches content virtually instantly.
It might be fancy to shit on everyone else's work, but this only happens if you lack objectiveness and sincerity.
The first element of each list is compared, and if they are not equal, the comparison result is determined based on the comparison of the first unequal elements.
In this case, [1, 2] and [10, 2] both have different first elements (1 and 10). Since 1 is less than 10, the comparison result is True.
The second element of both lists (2) does not affect the comparison result because the first element is already sufficient to determine the outcome.
[1, 2] < [10, 2] evaluates to True.
I like that typescript does not rely on an implicit choice, but let’s me express exactly what comparison I care about.
This has been a suprising thread to me -- I just assumed "everyone knew" that the vast majority of languages do lexicographic comparision of lists.
I will say typescript does "rely on an implicit choice", it has a default implement (the "convert to string"), which I'm going to be honest, doesn't ever seem like a sensible choice to me -- although maybe it feels more natural to javascript/typescript people.
My personal upset (I lost like a day to this) is that if you keep your numbers under 10, you do get the lexicographic ordering, as then lexicographic = string. I had a bunch of unit tests (all using numbers under 10), just larger inputs kept breaking, and it didn't occur to me to go read the docs for < :)
Something or some collection of people are doing something wrong, somewhere in the chain. So isn't this just yet another way to further entrench the modern state of meh-ness in performant UI?
Was insert-startup-product-built-on-.NET fast 15 years ago, for example?
Not trying to be controversial, just wondering.
I agree that a product built by clueless developers stringing together random libraries or bad custom code in React would create slow products.
In your spirit, I might argue that thinking "reactively" and client-centric tends to lead to unneeded requests.
Developing without meaningful API contracts or basic CS skills also makes it easy to unintendedly blow up network payloads.
Caching is hard though.
Have you worked on bad PHP applications that reload on every interaction without caching?
So apps without network latency are faster? Huh.
That sounds like blindly throwing money at the software architecture problem you created for yourself. Supposedly SPAs became popular because your line of reasoning was embarrassingly absurd, in the sense that you do not mitigate the penalty of a network call by microoptimizing the cost of a network call.
Microbenchmark to compare with typedload (which I wrote) and apischema https://ltworf.github.io/typedload/performance.html
Note that pydantic and apischema use .so files, while typedload is just pure python code.
I attempted to build a startup product using a vanilla JS SPA when I was a founding engineer, and the result was predictable: it worked great for me, but nobody I hired wanted to learn some random guy's vanilla JS codebase. I've since resigned myself to using React simply because that's what developers expect, despite all the headaches.
For what it's worth, we ended up migrating to Mithril in the V2 of that startup's product. We all enjoyed Mithril and it scaled really well for us, but my team did have some apprehension about their React skills falling behind.
This hits the nail in the head.
A single high-res background image weights more than all the code in a complex webapp. If that image is required to pull the first contentful paint then the page will feel slow.
It matters nothing if your JavaScript is just a hello world console log. That background image is in the critical path.
Browsers impose a limit on concurrent requests (usually 6 per hostname) for a reason - HTTP requests, be they too many or too large, are one of the largest performance problems in a typical site. Just because it's easy to fire-and-forget an HTTP request doesn't mean it doesn't have a performance impact, especially in apps that are already network-chatty. I think you know this because you go on to list "the volume of data we're transferring" as a performance bottleneck, and obviously that's done through HTTP requests. Also, in the real world analytics installations virtually always include installing a third party library i.e. google analytics, so you're taking hits to download the library, interpret the source, run the code alongside your app, usually in the same thread, all before you've fired any of those HTTP requests.
> A/B tests just means settings/feature flags and metrics
I'm aware of the definition. If you are implying that conditionally loading settings or feature flags and tracking associated metrics doesn't have a performance impact, I would disagree. It is usually possible to implement a given A/B test with minimal performance impact, but in practice these are absolutely a contributor to the kind of bloat I'm frustrated with.
> sessions are recorded since ever with zero performance penalty
It sounds like you're talking about logs. I'm talking about "session replay" features provided by companies like FullStory and Sentry that allow you to replay every mouse move and keystroke of your users.
> It's trendy to shit on the status quo but it also is low-effort and lacks any insightfulness.
By this point in your comment I think you forgot what we were talking about. I was arguing that the architecture of modern SPAs shouldn't bear the blame for bad performance; my point being that whether or not we continued to use server-generated HTML we would still be suffering from similar problems of bloat today. The "status quo" that I'm shitting on is taking lazy approach to performance management and injecting way too much cruft; unless you work for an analytics provider I'm not sure why you would be opposed to that stance.
Requiring the max old space flag is already too much. It's annoying.
Python does indeed have some runtime type safety. Way more than any JS runtime I've used. And I've written way more Node/JS/TS than Python.
In terms of the compiler, Nah. The whole ecosystem stinks. I cannot wait to move my larger Node projects to Java or something else. Idk how much dev time people waste on arcane tsc or npm issues but the answer is "too high" regardless. At least esbuild is okay.
Rust is neat. I wrote a decent sized side project backend (weatherfy.com) in Rust, with sqlx, postgres, and axum. Shared cache was tough to figure out but everything else was easy.
The whole backend has like one lifetime annotation, so that wasn't too bad. Honestly, I'd use it again, but the compile times scare me.
Rust is for when you really need to perform manual memory management. If you don't need that, then a GC language is better.
If you're choosing manual memory management over GC, you better have a really good reason to do so or a really small application that you're writing.
javascript is terminally single threaded, with the only solution being either multiprocessing and message passing, or cooperative multitasking style promises/async. This is not a situtation where javascript pulls ahead, as hard as python seems to try to fall behind.
You admitted your opinion would be controversial. Guess what? It is. Hardly anyone wants to do ML in TS. No ML researcher <wants ? <to> | deal | with < type signatures > (like ? This)>>>>
> No.
I mean.
>>> 1 + "1"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'> the insanity of
welp, so much for that.
- New libraries can't just be deployed to the OS, they must be compiled into the project.
- A check for thing==nil doesn't always work for all types of nil. Yes nil does not always equal nil.
- Panics aren't necessarily a bug. They could be a feature (?). Crashes are always a bug in C.
- Garbage collection is mandatory in go. C people are used to managing memory themselves like going for a walk. It's kinda natural.
- GC causes hard to debug performance issues. This was true in Java. And it's true in Golang.
- Golang channel primitives has bugs of their own with deadlocks and other interesting behaviors, whereas a lot of the threading issues in C are pretty well understood.
Incorrect. The compiler forces you to handle memory properly. It doesn't, as far as I know, manage it for you.
The burden is on the programmer to do it correctly. The compiler stops the programmer from doing it incorrectly.
Compare with GC, where the programmer is freed from the burden of worrying about memory altogether.
The first part Rust does for you.
The second part you'll indeed have to do yourself and the compiler will force you to do it safely.