There’s a small comment here about progress in my research around adding a pretty printer for Hell. You might find the idea of an interactive lazy printer as intriguing as I do!
Cool. With a longer delay do you batch and redraw or drop events? And do you default to lazy evaluation or is there a specific depth budget at which laziness kicks in?
So the actual re-drawing happens automatically when you update the state in brick. It has a diffing algorithm for the TTY state. I could still batch changes, but don’t presently.
The laziness is universal, but I was thinking about adding optimistic eagerness to any slots that are atomic like numbers, text, etc. Even then, those fields could be expensive to compute or not actually terminate, and in that case the system would handle it just fine and report it as ongoing, or cancelled (by eg timeout or user interrupt).
Nice! Instead of artificial 100ms delays, could one squeeze an IO call to network between the parts of the data structure? I’m asking because especially in SCADA protocols the address range of all available data is often arranged as a tree, with leaves from a finite and statically known set of value types (like Present’s Value). Protocols allow you to “subscribe” to certain leaves of the tree and receive updates. There, the same problem of batch processing arises where you don’t want the display to cycle through a hundred delayed updates when the viewer is only interested in the latest one, at the end of the queue.
I added the artificial delays just to check that the user experience works when there are delays for evaluation.
I’m not sure this answers your question. I’m not familiar with your domain.
However, one silly idea I had was that values of type IO a could be evaluated on demand and then the results displayed inline as yet more presentations. Not the same, but does involve I/O.
I’ve reached full entropy in my test implementation with brick. So I’m going to discard and make a fresh implementation based on what I’ve learned, time permitting.
That’s exactly what I had in mind. Does that make the idea less silly then? Suppose
data Value = A | B -- base types
data Addresses = Addr {foo :: Value, bar :: Value} -- description of API end-points
fetch_foo :: IO Value -- some network API call
fetch_bar :: IO Value
data Fetch = Fetch {fetchFoo :: IO Value, fetchBar :: IO Value}
-- we could express Fetch and Addresses in a common higher-kinded type
addr = Fetch fetch_foo fetch_bar :: Fetch
Since the fields of addr are lazy, the calls to fetch_foo or fetch_bar could be made on demand while presenting the structure in Hell.
So we both had the same silly idea. ![]()
Indeed, sketching this out for Hell, there’d be:
- Here a case that checks for any value of the shape
IO a. - An IO-aware UI could show your record like
Fetch { fetchFoo = [IO Value], .. }and[IO Value]would be a button that you could push, and it would kick off an async job to evaluate, execute and then present theValuepart, similar to the other async evaluation jobs, with the same lifecycle (running, cancelled, excepted, or succeeded). - Once the job finished successfully, it could replace the button with the value, or show below it so that you could re-run it if desired.
It gets all a bit beyond the scope of a simple scripting language by that point, but it’s a fun area of exploration. We use laziness for control structures and some handy data structures, but as a data exploration driver I haven’t seen that much.
I got sick of bash this week. So, I rerolled a program I was building in hell and I’m loving it. Great idea and implementation. It’s so much more readable than bash!
I’ve been having an interesting discussion with the community at NixOS about (some day in the very far future) replacing bash as an official nixpkgs scripting language and stumbled on this project. I eventually had the idea of forking and extending Hell to be a a custom nix language. It’s definitely an interesting idea, IMO. Do you have any thoughts about how it would work or be extended to do nixpkgs? I had the thought that it could add Nix-specific primitives to supportedLits/polyLits, add a StorePath type, etc.
Edit: I’m doing a rolling fork to try out these ideas on a different branch.
I didn’t see a license. Please advise.
Always wanted to try hell on a small to large setting, but it’s currently marked broken in nixpkgs (probably because it’s the penultimate, not the latest version).
I’m just travelling at the moment, but I’ve added a LICENSE file to the repo!
Happy to reply more in detail later, but forking and using as a base for whatever purpose would be great! I’ve kept the implementation all in one file so that it feels like something one can just grab and rework to their own needs.
Thanks so much! I’m getting a fairly stack-overflowesque reaction from the NixOS people but I’ll keep plugging away because it seems like a really fun experiment and I’ve got all the time in the world to play around with code this week.
One guy that liked the idea wanted me to change :: type devs to : I obviously had to refuse. ![]()
I really love this scripting language. It’s really satisfying to read especially compared to BASH and already caught a few issues that I had in the script I changed from bash to hell.
(probably because it’s the penultimate, not the latest version).
Good catch. haskell-updates branch has the latest release which does build
If you’re in a rush to try hell out you can try to jailbreak the penultimate version on your nixpkgs pin, seems to build fine on master branch with:
hell = unmarkBroken (doJailbreak super.hell); # old base bound