HN user

grayrest

1,950 karma

front end guy

Posts1
Comments681
View on HN

There isn't much in the way of libraries for Roc at the moment so the split is basically library vs application for my platform. I've been in the Zulip since well before the split but I've been waiting for the new version aside from playing around with the WIP when it first started working for Advent of Code. Reports from the past few weeks indicated it was close enough to ready for me so the web server is my first real effort. It's not generally ready (e.g. the compiler is crashing pretty regularly for me as I push into less common language features) but I'm enjoying myself and I do like the language design.

There is plenty of room for a more interesting nuance once the ecosystem grows:

I'm pretty pleased with my server platform so I'm making a stab at UI with a platform that's Clay and Solid2 ported to Rust. The Solid 2 model has pervasively asynchronous signals so components are written as if they're permanently live and simply don't get run until the constituent signals are ready. My thought process is that this is technically a pure model and only the input changes and effects are impure so there's a pretty clear Roc/Rust split. The platform is still in the assembly process so no actual experience to report. I'll be trying to avoid it but I expect to be doing code generation/compiler hacking in the effort.

On the other side is Luke's roc-signals [1] which explores how the signals model works if all the signal engine code is in Roc with only a minimal backing platform for holding the mutation: "We may not add dataflow analysis passes, dependency-graph extraction, or any new compiler behavior. Everything is ordinary Roc plus a Zig host."

[1] https://github.com/lukewilliamboswell/roc-signals/blob/main/...

If any Roc devs are around I'm curious about the use cases for Roc.

It's a general functional programming language that's interested in the constraints and state control properties but not really in the dogma/traditions. As a specific example, it has a for loop statement that doesn't return anything just because sometimes the algorithm is easier to express imperatively. That said, it really is functional, mutating functions/methods require a `!` suffix and `->` (pure) vs `=>` (not) is distinguished in the type system and enforced. The language is fully decidable so type annotations are optional with the arguable exception of the built-in Serde which needs a concrete type to encode/decode. It's also pretty fast, like in the Go range.

I think it has the best error handling of any language in the ~3 dozen I've tried. It's Rust style in general with `Result` renamed to `Try` but the error side of `Try` is an open tag set and can just aggregate so you get the nice parts of the Rust error experience without the downsides. As an example, a coeffect (effectful input) from an example on my server platform:

    book! = |req| {
        body : { id : I64 }
        body = Req.json_body!(req)?
        rows = Sql.query!(req.ctx, db_path, "SELECT id, title, author, year FROM books WHERE id = ?", [Integer(body.id)])?
        row = Sql.first(rows) ? |_| NotFound("book ${body.id.to_str()} not found")
        book = decode_book(row)?
        Ok(book)
    }
The full set of errors covers malformed utf8, missing/wrong type for id, db errors, the custom NotFound with message, and missing/changed db columns and these plus all the other errors across the app get rolled up and handled in one spot by the error mapping function which rolls the input errors to 400, a 404 for the NotFound, 500s in general in a big match. I have more compact ways to express this in the platform (sqlx) but those don't show off the error handling as nicely.

All in all, it's pretty much just a nice hosted language for doing things.

Do you see it competing with WASM for the plugin use case?

It's mostly competing with Lua and friends but the host is a platform and not an embedder so the Roc goes on the outside and produces the binary. Roc is particularly well suited for compiling to wasm because all the effects coming from the host is shared. This is actually one of my primary interests in Roc but I haven't really harassed the Roc team about it because they've been busy with the rewrite and wasm module specs have been WIP.

Why would an app author prefer to expose a Roc layer to their app rather than a WASM layer?

No need for the relatively large WASM runtime would be one of the first ones but Roc isn't really designed to be embedded. I expect to mainly use Roc for app level code on top of Rust for systems level code. I could write app-level Rust but I like functional programming, GC (refcount) is convenient, the error handling is nice, no annotations are nice, super fast compiles are nice, etc.

Do you see it competing with Gleam for server side http code? Do you see it competing with Elm for client side code?

Sure. As mentioned, I'm experimenting with a server platform that uses pure handlers plus an effect system. I have a RealWorld implementation and in casual benchmarking on my M1 laptop I get 69k req/s for the article endpoint (serialization bound) and 10k going through the article_list endpoint (sqlite bound, 4 table join). The framework also has full and automatic cache invalidation so if I turn on caching I hit 120-140k req/s on both endpoints with no other code changes.

As for GUI stuff, I'm working on a platform (Clay+Solid2) but I don't see any particular reason it wouldn't work.

The thing that interests me the most is that execution is deterministic. If the inputs to a WASM module are logged you get durable execution and rr style reverse debugging as part of the package.

I'm particularly delighted that the fold is unique; I've never seen anything similar. Thanks for sharing; this is the sort of thing I look for on the Internet.

This depends on the metro. NYC generally doesn't care for the trains/subways so they only make a difference on buses.

I ride a Reise & Mueller Birdy Mk3 mostly because I think folding bikes are neat and the Birdy has my favorite fold. The other rationale was that I wanted a nice bike I'd never have to lock up outside in NYC. The intention was to use it as a commuter since not spending on the subway would pay for it pretty quickly but shortly after I got it I started working remote so I've only done commutes on it for a couple weeks of gig jobs and it's mostly a recreational bike.

I went through an extended project to make it faster and wound up with a loop handlebar for body position, replaced the wheelset to move from 355 to 406 for tire selection and did the drivetrain at the same time to accommodate a 9-32 cassette. Between the wheels and the sub-11 tooth sprockets I can pedal up to ~26mph instead of ~20mph on the stock setup (good enough) and the low end is about the same. It doesn't perform like a race bike but it's pretty close to an endurance road bike. I do 20 mile rides a couple times a week on it and I've done a couple centuries.

The Birdy is my main bike but I'm a folding and recumbent enthusiast in general. The addition of the fold or moving the cranks in front of the rider means the obvious solution diamond frame doesn't work and I like seeing the creativity of the solutions. I've also owned a Xootr Swift that I gave away to my nephews, a Bike Friday Sat-R-Day folding recumbent for riding slowly in the parks, and a Baron Optima lowracer recumbent that I prefer for rides over 90 minutes.

I've spent years of my life absolutely alone; gone months without talking to another person. There isn't anything you'll do that will give you the emotional fulfillment of interacting with someone else.

As for living with yourself:

Find some sort of exercise you don't mind doing and make it non-optional. The goal isn't to go all out, just get your heart rate up for half an hour. You won't want to do it sometimes but you still have to go. You can do a crappy job at it and slack off for a day or two but you have to go out. For me this is riding a bike.

Otherwise it's good to be absorbed in something. It's not the same feeling but there is intrinsic satisfaction in learning / building / experiencing things.

I map caps to ctrl and do ctrl-[ to get to normal mode. The main reason is using Vim bindings in other editors where Esc can get intercepted by other bindings but ctrl-[ has always worked everywhere.

I'm sure I could learn to be slightly faster on dvorak/colemak

As near as I can tell, people generally type at similar speeds regardless of layout. If you're aiming for speed typing [1], I can see it making a difference, but chances are you're not going to see much of a difference. I type on an alpha-thumb layout (Hands Down Vibranium, the R key is under my left thumb) on an ergo keyboard. It's like using a fountain pen versus a cheap ballpoint or a nice mechanical keyboard vs a membrane keyboard. The experience is nicer but it's not a fundamentally different process. Being on a weird setup, I was concerned about "what if my keyboard breaks", "what if I'm on a public computer", etc and the solution is just to not forget qwerty. I use the laptop keyboard every other day or so for something short like a comment and it's fine.

What my personal setup does let me do is have my wrists in a neutral position with my arms resting on chair armrests and no active muscle effort. Between the efficient layout and small keyboard I never need to move my hands at all. This means that my wrists are just as well positioned after 12 hours as they are at the start and that's what has been letting my stress injuries recover.

[1] https://www.reddit.com/r/KeyboardLayouts/comments/1lfu1xt/fi...

CATL is claiming mass production of their sodium-ion batteries starts in December, with a target price of $10/kWh.

This got widely reported but there doesn't seem to be any source. I'll reference this video [1] to cover the claim along with a comparison to industry projections. Apologies for the video link but I don't have an article handy that addresses the topic as directly.

[1] https://youtu.be/KjiqqafD_0w?t=861

Rich Hickey had nothing to do with it. He did put a lot of the initial work to get Clojurescript up and running but for many years the majority of cljs maintenance and shepherding was done by David Nolan. Not sure now; I've been out of the loop for years.

The Clojurescript community was the first group adoption of React outside Facebook (early on they had community news blog post and mentioned it) but that because the React rendering model fit the Clojure data model. I was active in the NYC Clojure meetup at the time (Rich was from upstate and would only come in for big announcements, and David was local) and we had 4 or 5 months where there was active discussion among the web devs after the talk on how to make cljs actually work. My memory is Brandon Bloom is the one who made the React connection. David picked up the idea and promoted it to the wider community.

How did you find an ideal tent + tilt setup?

I had the idea that the best position for long sessions would be the most relaxed one so I put my hands in my lap on top of a lapboard (old Wacom tablet) and tried to put they keyboard halves under my fingers. I cut up a shipping box for the tenting stand. It took me eight iterations with three fresh starts but doesn't take that long with cardboard and tape. Stick the keyboard onto the stand with double sided tape, fill the cardboard pyramid with change for weight, and it's been like that for eight months. I'll come up with a more permanent solution at some point but it's working well so I'm not in a hurry.

The positioning was mostly about getting the wrists straight with the keyboard halves in-plane. The in-plane part was, naturally, the tricky part and that was mostly about getting the pinky corners set and then small adjustments until the index corners felt good. I have it set so perfectly aligned requires me to slightly lift my arms off the armrests which prevents sore spots on the forearms. At rest with the sides of my palms on the lapboard my fingers are off to the side of the keys but I can still type.

I've seen some members of the erg keyboard community design and print their own pcbs based on their hand dimensions

I follow the reddit community and see those as well. Someone started up a business selling fitted keywell boards (Cyboard) but the general consensus seems to be that the glove80 is good enough. I have some patches on my local ZMK (mostly changing chord detection) but no particular desire to do the hardware side. If I had a 3d printer I might consider it since apparently hand wiring isn't that hard if you have a print to hold the switches.

I have a Chocofi (36 key, 3x5 + 3 thumb per half). My complaint with the Cornes is that the keyboard doesn't have enough stagger for where they thumb cluster is positioned. Either the thumb cluster should move out like the ZSA voyager or more stagger is needed like Ferris sweep and most newer boards at a similar size including mine.

I'm curious but not particularly enthusiastic about keywells because I find the biggest improvement with a split keyboard is the tenting. My personal setup uses heavy tenting+tilting (basically half of a square base pyramid split on the square's diagonal) with the keyboard in my lap and my forearms resting on the chair arms locks me into a neutral wrist position without any active muscle effort. Keeping a good wrist position through the entire day instead of just the first half makes a noticeable difference.

Finally, I use the neutral thumb keys for shift on hold but I don't use any other thumb holds because I believe it has stress injury risks[1]. They're used for important but relatively infrequent keys: backspace, enter, tab, esc.

[1] https://getreuer.info/posts/keyboards/thumb-ergo/index.html

As an EDSF gamer (blame Tribes) I agree but I've run into issues with (IIRC) E, Shift, and Space on keyboards that aren't n-key rollover but it's been over a decade since I last owned one. So I think that's the reason for WASD.

FWIW, A is dodge for me and V is crouch.

While it's certainly possible to have a throttle most electric bikes require pedaling and therefore some degree of exercise. In many cases it gets people riding that would not be riding otherwise. For people that are actually interested in fitness it's a lot easier to maintain zone 2 on real roads with electric assist without the impulse to push it a bit up the hills. I did far more aggressive rides when I had access to my electric road bike than I do on other bikes because I knew I had a bail out if I pushed too far. Nobody's going to congratulate an e-bike rider on their feats of endurance or power but there are lots of points between a normal cycling effort and no effort at all.

There's a DIY forum for building those[1] but I think tow boogies[2] are more practical as a project. The idea is a battery box, controller, and motor on a boogie board and shifting the weight of the person being towed allows for steering.

[1] https://foil.zone/ [2] https://www.youtube.com/watch?v=S9z6BP8Y42U

Since the original article is about human power, I'll also link to this foil[3] which is for pump foiling long distance. I had run across the channel well before that and thought his goal for a half hour was goofy when people were getting 90s or 2 minutes so I was quite shocked when it actually got built.

[3] https://www.youtube.com/watch?v=WfJbF0xkUOY

Roc does not have computation expressions and this specific syntax is the "something similar". Many other comments have noted that this feature corresponds to the IO Monad with do notation in Haskell and Computation Expressions are F#'s take on monads & friends. Roc's take on this is basically "why should someone need to know category theory to write to a file" and the relatively simple "use ! when you want to yield control to the platform" suffices until the programmer decides to dig into a full explanation.

More generally Roc is following in the footsteps of Elm in that it's an exercise in functional language minimalism. I personally like computation expressions but I think they're beyond Roc's preferred feature complexity limit. There are proposals to expand this feature either using a `?` in a `with` section that would allow for Rust-like error handling with `Result`s or a type-dependent version of `!` so the syntax could be used on something other than `Task`s. Regardless, it's expected that the IO use case is going to be the primary use case regardless so I expect they're giving people some time to play around with it and provide feedback before making further changes.

However, this flexibility seems to be primarily used by language/framework implementers themselves. Not by application developers.

In the case of Roc this is determined by the platform and the platform corresponds to the framework level. A webserver platform is going to have a different set of execution choices than a cli platform or a platform for defining user functions in a db. So I wouldn't expect every application developer to make a platform but it's not going to be a compiler/stdlib only sort of thing.

As for the fractured IO, I'm pretty sure that every single person who has dealt with any functional language is aware of the propensity towards fractured IO ecosystems. The exact details of sharing code across different platforms in a useful way is somewhat hand waved at the moment in that there's a rough, unimplemented plan. I kind of expect that there's going to be some piggybacking on the WASM interface definitions but there are a couple changes around Tasks and the module system that are in the pipeline before that'd come up.

Note: I'm mostly an interested bystander

it’s not an angle of HCI I’ve examined explicitly, at least for very long

If you're looking for additional reading then it's arguable that Reacts matches this viewpoint but any web framework from the past few years using signals (re-popularized by Solid) is explicitly this approach. Most frameworks don't opt for graphical node editors and I've never liked the approach but I've seen a number of those as well.

More generally this falls into dataflow programming which has had a number of published papers starting in the late 80s I believe.

If you're looking for a similar computer sciency approach to UI structure you can look up statecharts which is an older idea that formed the conceptual basis for Ember Router and, in turn, most js framework routers in the past decade.

To be clear, I was intending my post to be exclusively about components. I was assuming people would get their preferred niche frames to put them on.

If I'm going to pick a fancy niche frame I don't want to afford I'd opt for a Jones LWB space frame. I'm actually a recumbent rider so the more likely expensive cycling purchase would be a velomobile.

Rivendell, Velo Orange, Rene Hearse and I'm sure I'm forgetting someone else in that group more or less do this.

I'm nowhere near an expert but I think most small (or steel) bike brands never moved off english threaded bottom brackets so I think that's mostly a major manufacturer issue. The brakes are frame dependent but calpier and cantilever arms are fairly simple/available or flatmount if discs (ignoring that Shimano just kind of decided they didn't like post mount). Finally, if you're comfortable with (ratcheted) friction shifting then most drivetrain compatibility issues are covered.

The components themselves tend to be pricier since they're targeting a niche market but there are people serving it.

It sounds to me like you didn’t understand the details of Svelte’s reactivity model

This is a reasonable assumption given what I've described. It's been almost two years but from what I remember the problem was updating what should have been the same reactive binding in two different locations. When trying to debug a problem I was stepping through the code and noticed that the indicies passed to `$$invalidate` were different. Checked for typos and shadowed declarations and found none. Rolled back to checkout and found the indicies matched. In the process of undoing/redoing my conclusion was that the introduction of an unrelated reactive binding was the difference. I could have been wrong; I was pretty exhausted at that point.

It sounds also like you’re fighting the framework.

Sure. In this case it was getting a big chunk of JSON back from an endpoint I didn't control and trying to use a number of reactive variables to select out pieces for display. I've done similar things in a half dozen other reactive/FRP libraries without issue but I assumed I was off the beaten path in Svelte.

I realize that this is empty complaining about other peoples' hard work. I don't have the code any more and I don't have a reproducible test case so I have no problem with anybody waving this off.

Why do you care about compilation output?

I was advocating/piloting a new framework in the app and was unhappy with the output versus the input and perceived complexity. I spend a significant fraction of my time cleaning up performance issues. A good chunk of that is download size so I care in general about what I'm sending across the wire.

Also, your component sounds cumbersome.

Sure. It was the classic feature creep scenario.

So my default is lean HTML, lean CSS, raw JS.

This is fine but having spent a decade of my career doing this, I would never advocate for it in an app with any kind of frontend complexity or a chance to grow into it. The frameworks mostly obviate the need to understand layout thrashing, resource cleanup, and retained state conflicts in the DOM. If you're doing simple stuff these don't matter but they're challenging to fix and more challenging to stay fixed when a group is contributing.

Svelte has caught my attention and I want to give it a go, but if the output is bloaty then that’s a red flag for me

Your use case is a pretty good fit for Svelte and you're likely to get output you'd find acceptable and given your preferences I think you're more likely to prefer its sensibilities over other frameworks that could provide the perf you're looking for. The tradeoff is kind of like the tradeoff between monomorphization vs virtual dispatch. The Svelte compiler is basically inlining the update code instead of using shared code in a runtime.

maybe someone can shed some light into the downsides of Svelte

Svelte trades off runtime size for component size. It was created in the context of infographics for the New York Times online and for projects that roughly line up with that it's pretty much the technically best option.

I like the Svelte authoring experience and introduced it for a few components in a React based low-code platform. The reason I phased it out was a chat component that was ~600 LoC and 50-something reactive variables in a moderately complex chain blew up into ~5k LoC of output. I also ran into what seemed to be some transient invalidation issues. I was short on time to debug this and engage with the Svelte community so I rewrote it in React to match the rest of the system. It's possible I was doing something wrong but I don't have enough confidence to bet on 3.x again. I'll take another look when 4.x comes around.

Presumably if Svelte were the be-all-end-all of front-end frameworks, it would dominate soon enough?

There's significant network effects around the established frameworks. Nobody gets fired for picking React. I personally think Solid is the best overall technically but the ecosystem and mindshare is smaller and that matters to a company making a business and not necessarily technical decision.

Solid works differently in that the output is implemented as a side effect of a subscription to a piece of state. Simplified, think of a signal as an event emitter and a variable with a getter and a setter. Using the getter adds its downstream effects–most notably adding/modifying DOM nodes–as an event listener. Using the setter updates the state and triggers the event listeners.

Most other frameworks here (all? not sure about Vue) will invalidate and rebuild an entire component subtree if a piece of state in them changes. The solid runtime doesn't care about components so changing a piece of state high in a subtree will only update the piece of the DOM directly depending on that state and not any nodes that were authored as sub-components.