Website contributor here! Answering some of the technical questions on Twitter: https://x.com/_chenglou/status/1826425720522080480
HN user
chenglou
https://github.com/chenglou/ https://twitter.com/_chenglou https://shadertoy.com/user/chenglou
Oh hey =D
The ring's source code in particular had to be transcribed from an actual shader I made (https://www.shadertoy.com/view/msd3R2). I didn't have the patience to write it directly in CSS. It's neither the right semantic nor the medium for it (which does make you wonder what CSS' medium was supposed to be).
Out of curiosity, I've transcribed that ring, which runs at <1 fps with pure CSS, into a JS version with requestionAnimationFrame + setting the background color (https://github.com/chenglou/pure-css-shaders-art/blob/master...).
Perhaps unsurprisingly, it runs at full 120fps on a decent laptop (it's just 21*21 JS style setting really). I've tried to make a half-JS, half-CSS version, and the speed is somewhere in-between. Basically, CSS variables are _extremely_ slow across all browsers, and I don't believe I've hit some particular edge-case. They're not used idiomatically here, but still, we shouldn't hit this drastic of slowdowns when updating 441 styles. They're also pretty hard to read the moment the calculation isn't some trivial basic arithmetics; and even then...
The true shader version runs at basically infinite fps =)
Yeah I was only talking about quantities. Equivalently, assume that it's a linear algorithm in the child and a linear one in the parent. Ultimately it ends up as O(nm) being some big number, but when people do runtime analysis in the real world, they don't tend to consider the composition of these blackboxes since there'd be too many combinations. (Composition of two polynomial runtimes would be even worse, yeah.)
Basically, performance doesn't compose well under current paradigms, and you can see Casey's methods as starting from the assumption of wanting to preserve performance (the cycles count is just an example, although it might not appeal to some crowds), and working backward toward a paradigm.
There was a good quote that programming should be more like physics than math.
It's more than that. The way black box composition is done in modern software, your n=100 code (say, a component) gets reused into a another thing somewhere above, and now you're being iterated through m=100 times. Oops, now n=10k
Generally, Casey seems to preach holistic thinking, finding the right mental model and just write the most straightforward code (which is harder than it looks; people get distracted in the gigantic state space of solutions all the time). However this requires 1. a small team of 2. good engineers. Folks argue that this isn't always feasible, which is true, but the point of these presentations is to spread the coding patterns & knowledge to train the next gen of engineers to be more aware of these issues and work toward said smaller team & better engineers direction, knowing that we might never reach it. Most modern patterns (and org structures) don't incentivize these 2 qualities.
Quite a few APIs use a pair of `{start, length}` instead, which in the context of the post's example, is even clearer. Empty interval would be `length == 0`, time interval would be a single array of `starts`, etc. Fewer subtractions (to get length) usually end up nicer too.
Personally I feel that Cappuccino is one of the last frameworks that still cared about a kind of interaction design that’s no longer discussed on the web, replaced mostly by more devops/abstraction-oriented discussions.
I manage a web UI programming language in my free time, and the juxtaposition of folks claiming FP ergonomics benefits, then upon my request, showing a static, interaction-less end result whose improved version would obviate their pristine architecture, is pretty staggering. The typical defense is "hey we're not designers" but if you zoom out a bit you realize the whole environment doesn't foster engineers to care about design concerns anymore (barring a niche but valuable vertical of optimizing for payload size). This in turn puts pressure back onto designers who come to expect less and less of what they care about on the web.
Just the other day a newcomer shipped an animated row transition after fighting her framework for 3 weeks. The designer was delighted, but the manager didn't even get the point because he matured in whichever era of JS framework that de-emphasized acquiring taste in interactions.
I myself come from a Flash background, so rather than seeing an upward trend, I see a decline in UX concerns, followed by an incline of devops-related concerns in UI frameworks (accompanied by HN comments saying that in both cases the web should have stayed as a document format, only to end up with an awkward mix of document + app architecture their desktop apps through Electron anyway).
If I were to categorize these "eras", I'd rather take the perspective of wondering at which point, and why, framework process ended up more important than the product. Heck, a similar thing is happening on native too, unfortunately. Where did all the interaction designers go?
Maybe AR would nudge more folks to learn and focus on rendering, gestures, transitions, framerate, intent and the rest.
No not really. It’s just a bunch of spans styles
Some upcoming languages like Zig and Jai have features to allow you to easily switch from array-of-structures to structure-of-arrays at writing time (https://www.youtube.com/watch?v=zgoqZtu15kI). Some edge cases also require rather unique ways of chopping up data, that wouldn't lend themselves well to be shoehorned in one of the canned categories of transforms.
Although constraint declaration is orthogonal to data-oriented development, I'd say that the philosophy of using generic constraint solvers (or other overly generalized CS ideas) goes against the spirit of DOD, which is to "just simply do the work". It is after all reacting against object-orientation (and even modern FP) which tended to think too much in the abstract and worrying too much about some form of taxonomy, as opposed to coding against plain data.
I help maintain ReScript (https://rescript-lang.org) and we've been rolling without an import statement for years now (basically OCaml's module system). The default is to just write `MyModule.doThis` at callsites. Sometime you do wildcard open (`open MyModule`) within the right scope. Sometime you do it at the top level for convenience (e.g. stdlib), but folks try to be judicious. And yes, you can alias a module path, e.g. `module Student = School.Class.Student`. Worth noting: the reason why fully qualified `MyModule.doThis` can be the default, is that we _usually_ dictate filenames to be unique within a project, so there's no verbose path issue nor refactoring issues (and yes this works fine... Facebook does it too).
Static analysis-wise (which is what most of the blog post's about), things are basically the same. The tradeoffs are mostly just in terms of readability. I used to be ambivalent about this myself, but looking at the growing body of GitHub TypeScript code with mountains of imports automated by VSCode, imo we've landed on a decent enough design space.
Jai is, imo, doing it right! For custom iteration one really _just_ wants it to be a little syntax sugar over a regular loop. Most other languages end up constructing some paradigm for iteration that drags in OO, FP, some interface concepts, generics, etc. Never mind that some edge-case async generator iteration can't leverage the `for` syntax; they deserve to stand out and be examined anyway. Especially in the case of Jai, where you'd want assurance that the for loop isn't incurring some weird function, allocation and other hidden overhead. No sufficiently smart compiler needed.
Compare that to e.g. Swift's iteration, with corners of undefined behaviors, potential allocations, and a collection types hierarchy that feels more like doing taxonomy than just looping.
Though to be fair, Jai's loop is rather intense in its usage of a macro system's features.
Related: Common Lisp loop macro: http://www.ai.sri.com/pkarp/loop.html
In case this isn't clear, RefTerm is:
1. Faster in every case tested
2. More fully-featured, including i18n
3. Easier to read and maintain (see for yourself)
4. Shorter
5. Using existing libs, aka interops well
...So none of these typical hand-wavy dismissals apply:
1. "Is it really faster for edge cases"
2. "He probably didn't implement certain features like Arabic and Chinese"
3. "Businesses just wants enough"
4. "Businesses just wants enough"
5. "It probably makes some closed-world assumption"
The performance of RefTerm didn't come from some big tradeoff; it came from using the right perspective and keeping things simple.
Sure, past a certain complexity threshold, you'd have to work for extra perf, but from my observations, folks who immediately jump to the wrong implicit conclusion that "good perf must have required compromises" got conditioned to think this way because the software they work on are already (often unnecessarily) complex.
Related, on HN recently: How Doctors Die.
https://news.ycombinator.com/item?id=28463482 https://news.ycombinator.com/item?id=26685244
Oh for sure, build is the main point and SoA isn't. The SoA feature discussions probably came from its deprecated interesting approach.
Collapsing into a single layer is great. As an aside, another language notorious for doing so would be Smalltalk, which is on the complete opposite end of the spectrum in terms of paradigm. It's kinda interesting to see the horseshoe theory in action in software philosophy.
The new way Jai does SoA is through just userland macros: https://www.youtube.com/watch?v=zgoqZtu15kI
Tldr SoA isn't a single transformation. Depending on your data access pattern, you might want to shape the structure differently. Kinda like GPU. So first-classing a particular pattern of AoS->SoA conversion (the one we often see in tutorials) wouldn't have been enough. In this case, a little bit of tasteful usage of macros covers the variations well enough.
There’s a much vaster gulf between a truck driver and a truck engineer, vs a Java programmer and a programmer one layer below. Per your analogy, the truck driver is the program user here, not the program maintainer.
You're right; the medium definitely shaped the message here. I've worked with and near many native and web teams, and have observed the engineering conversations of the folks from entry level to senior level. The difference in perspective and execution is notable each time. More interestingly, a transition transition from one side to another also changes perspective and conversation.
The community culture obviously also influences these discourse, but a surprisingly large chunk of the regard or disregard for UI/UX comes from using the tools you're provided by the platform, so much that unfortunately (or fortunately, if you've chosen the right platform), these factors influence one's learning and result much more so than even hard work and talent. A change of perspective is worth 80 IQ points, like Alan Kay said.
I help maintain ReScript. That it _seems_ to change the way code is approached is an artifact of an early community excited to try FP concepts in it. Nothing wrong with that; however, the ReScript team (me included) don't really approach things this way. Our goal, outlined at (https://rescript-lang.org/docs/manual/latest/introduction), is to provide:
- a more solid type system - and a much faster compilation speed (a growing pain in TS projects) - to write mainstream code - for products (as opposed to a focus on intermediate FP libraries)
The fact that it leverages some FP concepts here and there is just a mean, not the end.
The marketing of this is really hard, because FP usually attracts the crowd you'd expect, at the expense of several of the above emphasis. But we're still working toward it.
Having seen some ReScript codebases in the wild, I definitely know where your team comes from. We do try to approach it with the "different TypeScript" angle, but understandably userland doesn't always write code the way we'd like to recommend.
In the spirit of https://news.ycombinator.com/item?id=25846479, we’re trying to become a boring technology. Unfortunately boring technology had to have gone through a hype period.
I happened to have read many of VPRI’s papers and some domain expertise in a few of their topics, or know close friends with such domain expertise. Btw, my collection of their links are at https://news.ycombinator.com/item?id=19844088
The goal is a noble one; though it's unclear to me if their research is the right way to go.
Alan Kay's gang's research's end result are often great, but their implementations are usually more geared toward prototyping rather than production. For example, the GUI innovation is great, whereas its implementation using Smalltalk is clearly geared toward faster iteration and not meant to be as competitive as a production language (let's not enter into this debate as this is another rabbit hole. Plenty of other HN threads on this). But I think so far these implementations have gotten a pass because the end result is futuristic enough for someone else to come along and implement e.g. the GUI in more production-oriented languages.
In the context of VPRI, however, the goal wasn't to create more futuristic end results, but to recreate existing result using hopefully drastically more concise paradigms and DSLs. In this case, the implementation itself is the thing we should examine, and imo they kinda fall short.
For example:
- Yes, it's very appealing that Nile can reduce the antialiasing logic by so much. Is it resilient to real world requirement changes though? Unclear (and the Nile paper still isn't out as the author seems busy with some startup).
- The OMeta parser tool is cool until you encounter more complex grammar and proper error messages (most non-hand-rolled parser tools' error messages and error recoveries are treated as secondary concerns).
- Reusing the same state machine from OMeta (afaik) for TCP is clever, theoretically sound, but likely also not shippable in production the moment you want to tweak some low-level details.
- The dynamic language used for the final product, they baked in some pretty hardcore first-class features but..., this is too long to explain, though experienced folks who read this probably know what'd happen to such language in production.
- The bulk of the final line count reduction wouldn't come from some language-related line count reduction, but from VPRI's rehash of OpenDoc, aka the end product is a Word/PowerPoint hybrid put together by massively (over)reused component. The moment such app hits the real world and a component needs to deviate from its use-case in other callsites, that amount of reuse is gonna decrease quickly. HN has plenty of comments regarding OpenDoc so we can check history here. The final use-case being an OpenDoc rehash is also slightly cheating imo. Demo a game. The line count there is a better stress test.
- Real-world perf requirements are gonna increase the line count by a lot. I understand they're employing he classic PARC strategy to "iterate as if you have the supercomputer of tomorrow, today", but plenty of languages today can already reduce their line count by a lot if perf wasn't considered.
All in all, I'm not sure VPRI's respectable goal can concretize through their method. I am however extremely on board with their goal (of shrinking the code size to be understandable by as few people as possible). Here's an alternative take on the same goal: https://www.youtube.com/watch?v=kZRE7HIO3vk and of course Casey's friend Jon Blow whose language Jai so far obsoleted a bunch of other DSLs he'd have usually needed in his workflow (https://www.youtube.com/watch?v=uZgbKrDEzAs). Now that's a more realistic way of shrinking code and simplify, imo. Between:
- a great language that can demonstrably do low to high level coding and with an in-language metaprogrammmming facility that removes the need for DSLs, and
- VPRI's opposite direction of proliferating many DSLs (which in real world are gonna cause nontrivial accidental complexities),
I'd take the former.
No good generalized solution for this it seems. The best you can do is to specialize per situation and leverage all the assumptions you can while being cautious not to leverage the assumptions you can’t.
This problem contains lots of incidental complexity and it doesn’t help that folks do the opposite of the above and add a ton of accidental complexity on top. For example, for a situation with a crossfade or morphing set of 2 buttons you should:
- leverage the assumption that there are only 2, and static, buttons. - not assume that there are only 1 button present at any time.
Some end up inclined to create some “reusable” abstraction on top and end up doing the opposite: - generalize to handling n dynamic buttons. Without realizing that some important properties of that specific situation would have been lost. - (usually due to the lack of experience/focus/interest on the problem) oversimplify and assume there’s only 1 button present at any time.
Parent meant to say that practice makes perfect.
I've replied earlier with concrete details in another place in this subthread. You indeed haven't insulted anyone, but your comment history does indicate some biased intent; if the ecosystem changes affected you, we'd like to apologize for those; let's bury the hatchet here because maybe we can both agree that drama isn't productive.
We did use Reason and ReasonReact. Not sure where you're getting your info. And no we don't use TypeScript on the team...
...who are you? Everything you said in this subthread is false, and you seem to have a weird chip on your shoulder regarding this.
Jonathan Blow recently talked about this in https://www.youtube.com/watch?v=pW-SOdj4Kkk. Maybe this is where the author got the idea of noting them down?
These discussions usually invite lots of hand-wavy complaints and oppositions without more concrete progress. Out of boredom and to further better dialogues, I've tried to address every bug in that list:
iOS 14 discharged phone battery 80% to 20% during the night (no activity, much worse than iOS 13).
There's a new AI system since before 14 that monitors your battery usage and e.g. refrains from charging during certain times, among other features. It's likely that this system got tweaked (as opposed to sudden battery failure and recovery).
YouTube.app randomly scrolled to the top.
Dunno about this one. Did you touch the status bar at the top of the screen?
Instagram reset scroll position after locking/unlocking the phone.
Probably forgot to add that bookkeeping to the before-locking hook, and/or the hook before getting evicted from memory.
Race condition in keyboard in DuoLingo during typing.
Don't use DuoLinguo anymore. Can't comment.
AirPods just randomly reconnected during use.
Bluetooth?
Shortcuts.app stopped reacting to touch for ~30 sec.
Hard to say. Undefined state/exception bugs maybe.
Wondered why my apps were not up to date, found nine apps waiting for manual button click.
Push/pull model problem, battery conservation heuristics, server's notification scaling being best-effort, etc.
Workflowy cursor obscured by Workflowy toolbar, typing happened behind keyboard
Workflowy's iOS app uses web technology. The keyboard + floating bar layout is a recurring problem with said tech.
AirPods showed connected notification, but sound played from the speaker.
Bluetooth...?
Passcode unlock worked for the third time only.
Never happened personally. Can't comment.
Overcast widget disappeared while switching to another app.
That one's almost 100% in the animation system's bugs introduced in iOS 7. Tldr uninterruptibility + special thread/process causing extra undefined state + GPU.
YouTube forgot video I was just watching after locking/unlocking the phone.
Same as the instagram diagnosis.
YouTube forgot the resolution I chose for the video, keep resetting me to 360p on a 750p screen.
Dunno. No longer use YouTube app. Network? Sometime the UI can be misleading. The quality option might be just a best-effort option that it doesn't guarantee to respect. Someone else check this.
1 hour lost trying to connect 4k @ 120 Hz monitor to MacBook Pro.
Definitely not enough stress/integration testing, so it's unsurprising that anything might happen. Sometime provably impossible to get right due to neither party controlling the whole stack.
Workflowy date autocomplete keeps offering me dates in 2021 instead of 2020.
See earlier. It's not using the native NSDate (workflowy uses Momentjs). Plenty of room for error. NSDate's api usually won't nudge toward things like off-by-ones (I think).
In macOS context menu, “Tags” is in smaller font
Intentional.
Transmission quit unexpectedly.
And slowy =P. Likely due to exception/mishandling of memory.
Magic Trackpad didn’t connect right away after boot, showed the “No Trackpad” window.
Lots of preemptive races possible here.
Hammerspoon did not load profile on boot.
Dunno. I don't use it.
Telegram stuck with one unread message counter.
A few other chat apps do that too. Often from the denormalization of unread messages count in DB. That or something about the notification system.
Plugging iPhone for charging asks for a software update.
It's a feature, not a bug ™ =).
Dragging an image from Firefox doesn’t work until I open it in a separate tab.
That dragging is reimplemented using cross-platform tech I believe.
YouTube fullscreen is disabled in an embed.
Intentional. This is an option the embedder needs to opt into.
Slack loaded, I started typing a response, then it reloaded.
Depends by "reloaded". Without further description, it might be either a crash + browser-driven page reload, or some long in-app rerender due to React, state and network.
Twitter was cropping important parts of my image so I had to manually letterbox it.
Quite a few pieces of drama surrounding this recently. Won't comment.
TVTime failed to mark an episode as watched.
Never used it.
Infuse took 10 minutes to fetch ~100 file names from smb share.
No batched api + other shenanigans. Happens to Reminders too.
Tup requires FUSE, so is a deal breaking dependency for many corporate setups due to security.
If you’re into Tup, you might also like https://github.com/droundy/fac
Fun fact: ninja was inspired by Tup, but is a bit more geared toward pragmatic usages.
Turns out, we end up evolving said general intelligence system into doing bespoke creative work, and it slows down the compilation time by around as much as it takes a human to think up the analytical solution =P
Then we end up with a tunable knob to speed up compilation. But then this more or less reduces to coding up either method depending on the situation, aka what we began with!
I’ve had nothing but positive customer experiences from Bose (5 times, from 2-3 different shops). I’ve made a note to voice this feedback the last time they served me. Now I did =)
I no longer use the earphones, but I can vouch for them.
Argh, I promised myself I wouldn't come back to this thread actually; debates around this particular subject I've been part of have been tiresome =)
I can assure you that I'm not conflating simple things like user experience vs developer experience. That was the point of the asterisk.
I'm also very positive that the creator of Hypercard and Excel do _not_ think of their creation as appliance.
The whole point of the discussion around computer and computer languages/systems as tool for thought, is presuming that computing can be another form of mass literacy. Let's also not get into whether this should be the case (and my opinion here is less naive that one might think); I'm just saying that this is the debate. At the risk of exaggerating and drawing an imperfect parallel, the medieval monks also considered human language literacy as a "developer" thing, e.g. "reading the Bible is a separate domain to the everyday life of a consumer". And yes, they did make beautiful books as products/appliances. Again, I'm not saying programming languages will ever be useful to learn for the masses; that's another topic of discussion.
I agree very much that there is no general solution, and that tinkerers gotta tinker. Though this is orthogonal to the discussion (FYI I absolutely despise language tinkering/golfing these days; that's why I made sure to talk about this topic without talking about Mu earlier).
No comment about the Mu itself but:
Our approach to keeping software comprehensible is to reduce information hiding and abstraction, and instead encourage curiosity about internals
YES! Precisely. Most "bicycle for the mind" or "tool for thought" approaches go the completely opposite direction accidentally. Many programming languages and communities nowadays too.
In reality, if you wanna teach, you have to unveil the guts of the system. Sure, it's "scary" and all, but still, initial hand-holding & letting newcomers see the internals, rather than presenting an overly polished surface at the expensive of everything else, is a much better designed learning process. Design is "how it works" after all. *
Related: a common misconception when folks defend abstractions & encapsulation is to raise the issue that letting newcomers explore the internals causes too much trouble. That's an obvious strawman; these methods are not championing everyone writing anything everywhere. They can stay read-only, but public. E.g. an interface can expose its internal data types, without allowing consumers to actually leverage that (enforced through types or something else). This is easily doable, and you don't sacrifice learnability in the service of team programming sanity.
* This point seems to get dismissed quickly by folks who only care about visual polish nowadays. Imo this is naive. I care about visuals a _lot_, and we should definitely aiming for both learning polish and surface polish. But you can't let the latter calcify the former.
My advice for all the "tool for thought"/"bicycle for the mind" projects: all your opaque designs and small interface flourishes falter in the face of a properly exposed and clean-ed up paradigm, in the long run. This applies on a smaller scope to modern libraries too. That boilerplate generator you mega-fine-tuned to the point you delight yourself in guessing the user's mind, ultimately is a roadblock in the learner's thought process. Expose the boilerplate and document how you're automating it instead. You're trying to teach fishing, not to show off your own fish.
Another good analogy I've heard elsewhere is to ask whether you're teaching people how to cook, or just building a microwave. For a tool for thought product ask yourself if you ended up accidentally making "an appliance".