[HN Gopher] Ink: React for interactive CLI apps
___________________________________________________________________
Ink: React for interactive CLI apps
Author : brianzelip
Score : 120 points
Date : 2024-11-01 13:15 UTC (9 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| akie wrote:
| I'm unsurprised, disappointed, excited, and surprised at the same
| time. It's clear that the React paradigm works really well to
| model user interfaces, and as such it's no surprise that it can
| be used to build anything from desktop software to apps to
| websites to command line apps.
|
| Amazing work!
| seanw444 wrote:
| Saving the world, one GitHub project README banner at a time.
|
| I don't know how I feel about writing CLIs in JS. Just seems a
| little bit janky to me, and I don't know why. I wonder if there's
| any push to have something JSXish in Go, even if it requires a
| pre-compiler to achieve the syntax, just for CLI apps like this.
| Then again, maybe I'd just rather stick with something like Nim
| where you can just have a first-class DSL through the macro
| system.
| captnasia wrote:
| there's a few attempts, templ[0], gomponents[1]. Not for cli
| apps per se but for web dev.
|
| 0: https://github.com/a-h/templ 1:
| https://github.com/maragudk/gomponents
| seanw444 wrote:
| I've seen gomponents, but it seems very verbose. Although I
| guess that's what you're stuck with if you're doing it within
| Go.
| kaba0 wrote:
| I don't know, go has lackluster expressivity, and these pre-
| compile tools always feel like a hack in its case.
| ipsum2 wrote:
| I don't think its a Go-specific issue, its useful syntactic
| sugar. I would love an updated JSX for Python.
| ProfessorZoom wrote:
| a cli that has a useEffect is so cursed
| klysm wrote:
| It's there as an escape hatch :)
| teaearlgraycold wrote:
| I like useEffect though
| cupofjoakim wrote:
| I get that there's a lot of people only comfortable in the js-
| landscape, but I still think this is a weird tool for the job. If
| performance is a concern there's no way node is the right thing
| to start, and react just seems silly to me here. Should be noted
| that I do a big portion of my day to day work in react - no hate
| on the framework.
| slashdave wrote:
| Node.js is not necessarily bad, but React? What makes React
| great is the ability to embed the DOM in your app. For CLI?
| Square peg, round hole.
| lowboy wrote:
| React enables declarative UI code instead of imperative.
| That's one of its biggest values. Even in the terminal,
| especially as the complexity of a tool increases.
| hombre_fatal wrote:
| What makes React great is that you write a UI tree that
| updates when state changes instead of imperative code like a
| clearRect() + drawLine() cycle.
|
| This project and others like React Native show that it works
| outside of the DOM: you just render something else instead.
|
| Thinking that React is only helpful for DOM UI seems like a
| misunderstanding of what React is and what makes it great.
| MrJohz wrote:
| I'm not sure that's true. There's lots of ways to use and
| manipulate the DOM. But React is particularly special because
| it turns out to be a simpler way to think about UI
| components. A TUI has nothing to do with the DOM, but it's
| got a lot to do with rendering UI components. So taking the
| ideas from React and applying them to drawing elements in a
| terminal makes a lot of sense.
|
| Arguably it makes even more sense than with React. One of the
| problems with React is that the underlying DOM is a stateful,
| imperative system, whereas React wants to behaves much more
| like an immediate-mode system, where the whole UI can be
| dropped and re-rendered at any time. But the terminal is not
| stateful like the DOM, and a more immediate-mode paradigm
| arguably works better in this context than in the browser.
| coffeemug wrote:
| Node/V8 is insanely fast. I never quite realized exactly how
| fast until I worked on this: https://www.spakhm.com/ts-wolfram-
| bench. It's mindblowing how fast it is.
| wyager wrote:
| I don't find "performance parity with Mathematica" to be very
| compelling. Mathematica is a CAS system, not something I
| would use to write fast code.
| coffeemug wrote:
| It's not that V8 has performance parity with Mathematica.
| The Mathematica interpreter is written in highly optimized
| C by experts over many years. My barely optimized js/V8
| code has performance parity to _that C code_. That's what
| should blow your mind.
| Etheryte wrote:
| For most apps, CLI or not, performance is not really a concern
| so long as it's good enough. Obsessing over performance before
| your program does The Thing is a great way to never actually
| get around to doing the useful bits. Now don't get me wrong,
| I'm not saying performance doesn't matter at all, but premature
| optimization and all its friends apply.
| nicce wrote:
| However, before you build the Thing, you should note that the
| architecture itself does not make impossible to improve the
| performance later on, if there is a possibility that it can
| make The Thing much better in the end.
| johnfn wrote:
| How would performance be a concern for _rendering the UI_ of a
| terminal app? Surely that happens in less than 0.001% of all
| cases. And of course no one in their right mind is implementing
| the core functionality of their app with react state variables.
| (Right..?)
| adamscybot wrote:
| The "should we use node for the core business logic for my use
| case" thing is absolutely valid and depends.
|
| But you'd be surprised about the "react for the front end of
| the CLI" part. I used this thing a whole six years ago in a
| complex interactive CLI and it came off great to use,
| maintainable and ergonomic. React is just one framework that
| proscribes to the "UI as declaritive/composable trees" pattern.
| And that UI doesn't have to be web based. That pattern works
| for all UI's I've come across.
|
| Its the _pattern_ that is a good reason to do this. And not
| react /js landscape. That part is probably a bonus for many
| though.
| sirtimbly wrote:
| JS will eat the software world. So having better tools on par
| with charm in the go world would be great.
| haolez wrote:
| Or it will be the (huge) technical debt of tomorrow.
| throwaway106382 wrote:
| just because you can doesn't mean you should
| EddieRingle wrote:
| See also, Mosaic, experimental terminal UI using Jetpack Compose:
| https://github.com/JakeWharton/mosaic
|
| Since it's Kotlin, it has backends for the JVM, LLVM, and
| JS/Wasm, though curiously Jake recently removed the JS target
| because it wasn't seeing much usage.
| seveibar wrote:
| Ink is great for CLI tools and enables really great tools. People
| who are saying React only belongs on DOM are missing out- React
| is the world's most popular and powerful declarative programming
| paradigm. It's being used for native apps, VR, 3d, CAD and
| electronics.
|
| React allows you to mix declarative and imperative programming
| concepts together in an effective way. To give a sense of why
| this is important: Imagine a CLI built with XML. It'd probably be
| pretty verbose and have a lot of domain-specific configuration.
| There also wouldn't be a great typechecker. React combines the
| most robust type-system in the world (Typescript) with
| declarative style. It has issues, but being "built for DOM/web"
| is not one of them.
|
| RE: Ink. A simple example of where it is better than most tools
| is you can simply import a re-usable "ranger-style" file browsing
| component, and adapt it for things like exploring a database, a
| spreadsheet, or an API spec. The potential is very high. Hats off
| to the author and contributors!!
| cupofjoakim wrote:
| > the most robust type-system in the world (Typescript)
|
| This is very high praise for something that only provides
| safety at compile time and not runtime.
| oorza wrote:
| Sure, but ten years ago, if I told you the concept of a
| "bottom type" would be common knowledge with web developers
| or they'd consider type algebra second nature, you'd have
| laughed at me. If I were to tell you they pushed a Turing
| complete type system into the mainstream and then abused its
| computational power to do things like create value types,
| you'd ask me where I was buying my LSD.
|
| TS might not be the most robust type system in the world with
| no qualifiers. And it might not even try to be sound. And it
| might disappear at compile time. But none of that changes the
| fact that it took a bunch of concepts that only programming
| language dorks knew or cared about and turned them into every
| day utilities that the junior most developers use. And it
| doesn't change the fact that it is the only mainstream
| programming language with a fully Turing complete type
| system.
|
| Give credit where it's due. I say all of this as one of the
| aforementioned programming language dorks who took a really
| long time to get on board with TS, as I thought Flow's focus
| on purity made it a better choice.
| dragonwriter wrote:
| > Sure, but ten years ago, if I told you the concept of a
| "bottom type" would be common knowledge with web developers
| or they'd consider type algebra second nature, you'd have
| laughed at me.
|
| If you told me that was the case today with web developers
| generally, I'd laugh at you _even harder_ than I would have
| if you made that prediction ten years ago.
| oorza wrote:
| All the web developers I've worked with in the last four
| years understand the concept of a bottom type, even if
| they've never been introduced to the formal phrase,
| because `never` is the explicit bottom type in TS and
| appears all the time. You can't go very long consuming
| libraries written in TS before you run into it, at which
| point you become familiar with it.
|
| Similarly, a function that accepts (explicitly) `A | B |
| (C & D)` and then dispatches to functions that accept `A
| | (C&D)` vs `B` is, you guessed it, type algebra and is a
| common pattern in hot paths through every TS codebase.
|
| Just because the formal nomenclature is unknown does not
| mean the concepts are unfamiliar.
| bryanrasmussen wrote:
| OK, that's all well and good but I notice you still haven't
| told me where you're buying your LSD?
| EddieRingle wrote:
| They're building a product that revolves around React and
| Typescript and include an email specifically for receiving
| investors, so it's reasonable that they'd talk up both
| technologies.
| emoII wrote:
| Is there a type-system that provides runtime safety? I
| thought type-erasure was common practice
| daotoad wrote:
| Raku has a really weird but cool type system[1] that does
| both compile time and runtime checks.
|
| Because some checks are expected to be runtime only, it
| lets you specify types like "Odd integers" by writing a
| `where` clause.
|
| ``` subset OddInteger of Int where !(* %% 2) ```
|
| You can use multiple dispatch to separate dispatch from
| processing: ``` subset Fizz where * %% 3 subset Buzz where
| * %% 5 subset FizzBuzz where * %% 15 multi
| sub fizzbuzz(FizzBuzz $) { 'FizzBuzz' } multi sub
| fizzbuzz(Buzz $) { 'Buzz' } multi sub fizzbuzz(Fizz
| $) { 'Fizz' } multi sub fizzbuzz(Int $number) {
| $number } (1 .. 100)>>.&fizzbuzz.say;
|
| ```
|
| Or even use inline anonymous subsets when you declare your
| functions: ``` multi sub fizzbuzz(Int $ where * %% 15) {
| 'FizzBuzz' } multi sub fizzbuzz(Int $ where * %% 5) {
| 'Buzz' } multi sub fizzbuzz(Int $ where * %% 3) { 'Fizz' }
| multi sub fizzbuzz(Int $number ) { $number } (1 ..
| 100)>>.&fizzbuzz.say; ```
|
| Raku's type system is one of its features that will show
| you new ways of thinking about code. I advocate playing
| with Raku specifically for mind expansion because it has so
| many interesting ideas built in.
|
| [1] https://docs.raku.org/language/typesystem
| phpnode wrote:
| Out of all the type systems which provide safety at both
| compile time and run time, which are your favourites?
| seveibar wrote:
| Runtime safety is slower than compile type safety. Runtime
| safety SHOULD be opt-in if there's a performance penalty!
|
| For runtime safety, there are lots of frameworks that follow
| TS's standard, one of the best is called "zod" which allows
| runtime safety and complex types are inferred.
| angra_mainyu wrote:
| Not to mention: type Thing = string |
| boolean; const arr: string[] = [];
| function add(item: Thing, dst: Thing[]): void {
| dst.push(item); } add(false, arr);
|
| Is valid typescript.
| adamscybot wrote:
| If you are used to structural typed systems though this
| actually "feels" expected. Don't get me wrong, I know where
| you are coming from. But the realisation comes from nominal
| systems, which are different beasts. As long as it actually
| evaluates to the expectations in the head of the developers
| using it, then that's okay.
|
| However, regularly it's not the case -- especially with
| people moving from nominal systems.
|
| TS can't really be practically nominal when it has to be
| constrained by its compile target. So I guess it ultimately
| boils down to an anomaly/criticism born from the legacy of
| how web standards came about.
| consteval wrote:
| Nominal type systems are pretty much better across the
| board IMO. Much safer and much less to keep in your head.
| Modern langs like Rust or F# have very complete and nice
| to use type systems.
|
| But yeah unfortunately not sure if JS is an appropriate
| target for these type systems. Nim does it, but I'm not
| sure how safe it is.
| adamscybot wrote:
| One language that comes to mind is ReasonML. It doesn't
| have runtime checking. It's structural and compile time,
| but the guarantees are supposedly cast iron.
| https://reasonml-
| old.github.io/guide/language/type#design-de...
| type thing = | String(string) |
| Bool(bool); let arr: list(thing) = [];
| let add = (item: thing, dst: list(thing)): list(thing) =>
| { switch (item) { | String(s) => dst
| @ [String(s)] | Bool(b) => dst @ [Bool(b)]
| }; }; let newList =
| add(String("asd"), arr);
|
| This doesn't work, since the dst array has to be one that
| can contain both string and bool in the first place.
| hajile wrote:
| Some people here seem to have a hard time understanding that
| React !== DOM
|
| React is descriptive. It creates a virtual tree and tells how
| that tree should be changed.
|
| What that tree renders into is a completely different problem.
| On the web, it renders into HTML. On your phone (ReactNative),
| it renders into native GUI. In ink, it uses a render engine to
| concat and output the text results (it's a little more complex
| than that as it uses Yoga[0] today).
|
| [0] https://www.yogalayout.dev/
| seveibar wrote:
| I love Yoga (flexbox++ engine built by Meta), and yes most
| people don't understand that React is simply not coupled to
| the DOM!
| rkagerer wrote:
| Is there a good resource that explains what React is and how
| it works, without limiting the focus to just the usual web
| applications?
| hajile wrote:
| It's a bit older (and focused on web), but you can try this
|
| https://github.com/pomber/didact
|
| At a high level though, you create a tree of nested
| objects. Each has a handful of special properties (children
| which contains an array of child objects being the most
| interesting to users). The rest of the properties are used
| to interface with the rendering engine by passing it
| configuration and (usually) event functions it can call
| when something happens.
|
| That tree of objects gets passed to the rendering engine
| which turns the tree into some kind of output (DOM, Canvas,
| WebGL, Ink, etc)[0].
|
| When something happens (an external event, a timer, event
| from the render engine, etc), your code potentially changes
| the object tree. React then walks that tree and uses a few
| shortcuts to quickly detect what has changed. Instead of
| re-rendering everything, it can give a list of things that
| have changed and what the new values are. The render engine
| then decides how to make those changes happen and the cycle
| repeats.
|
| It's really pretty simple.
|
| [0] https://github.com/chentsulin/awesome-react-renderer
| acemarke wrote:
| In addition to the excellent "Didact" article that explains
| how to _build_ React and its internals, I have an extensive
| "Guide to React Rendering Behavior" post that explains the
| user-visible behavior and mental model of rendering:
|
| - https://blog.isquaredsoftware.com/2020/05/blogged-
| answers-a-...
|
| I can also recommend Dan Abramov's post "React as a UI
| Runtime":
|
| - https://overreacted.io/react-as-a-ui-runtime/
|
| and then of course the actual React docs for learning how
| to _use_ React:
|
| - https://react.dev/learn
| dragonwriter wrote:
| > React allows you to mix declarative and imperative
| programming concepts together in an effective way. To give a
| sense of why this is important: Imagine a CLI built with XML.
|
| Talk about a late-night-infomercial-level "let's compare the
| thing we are talking up with the absolute worst alternative
| imaginable as if that was the next best choice" argument.
| seveibar wrote:
| Sorry, my point was to illustrate that XML is verbose so
| data-representation isn't a good fit. Most languages
| implement some kind of language-specific builder-pattern for
| CLIs, which in some sense is "re-inventing" a declarative
| programming paradigm for the domain-specific task. Could have
| done better w/ the example!
| kristopolous wrote:
| XML is a much maligned and poorly understood technology. It
| lacks glossy pitch pages and hypemen like react.
|
| Things like XSLT are really quite good if you can get past
| "ew xml icky"
|
| You can inline different schemas in modular, bazaar like
| ways from a variety of sources and pull in different
| parsers that do different tasks.
|
| For instance you could have, say an object subtype and
| pretend you, a spreadsheet developer made something
| compatible with it.
|
| As the tool parsers the XML, it sees the spreadsheet, which
| it has never encountered however, the schema tells it where
| to get the parser so it dynamically loads it, passes off
| the subtree it doesn't recognize and lets your spreadsheet
| parser do its things.
|
| It's remarkably clean and elegant clockwork for distributed
| modular applications.
|
| Things like a mobile phones share feature would be a
| perfect use case: a wide variety of objects from different
| applications passing through the same schema with a share
| directive invoked that allows you to share in a variety of
| ways. XML can handle this quite magically.
|
| I've built systems like this with XML, it just works. I was
| like "damn. XML... Who would have thought"
|
| People just never learned XMLs killer features because it
| got a bad reputation as being the thing that out of touch
| enterprise Java bozos make messes with - which is true.
|
| But that's also why JavaScript was shat on for 15 years.
| You gotta be adult enough to look past that. Try to
| separate the merits of a technology from your perceptive
| competency of its users.
| mbreese wrote:
| In general, I liked XML. But the entire system seemed
| fragile. Parsing and validation were great. It was a
| great system for ensuring your data matched the schema.
| But if it didn't, it seems like it never failed
| gracefully. As in, you'd be unable to even open your
| spreadsheet document if some random web server was down.
| Or if you were on a plane.
|
| Just a few of the things I didn't like about it. But when
| the system worked, it was quite capable.
| dragonwriter wrote:
| > XML is a much maligned and poorly understood
| technology. It lacks glossy pitch pages and hypemen like
| react.
|
| The negative feelings about XML are not due to the lack
| of glossy pitch pages and hypemen pushing XML and XML-
| based technologies; they developed whole XML had those
| far more than React ever has.
|
| Some people may be too young to remember that, but...
| kristopolous wrote:
| I'm too old to forget it. It was the wrong kind.
|
| They were the kinds with a bunch of worthless
| certifications who go everywhere in suits. I expected
| nothing but expensive clunky broken complicated bullshit
| from them. And that's exactly what they gave you when
| they pitched XML so I ignored it for over a decade as
| some tedious wasteful time-consuming thing that dumb
| people use to rake in hourly contract dollars.
|
| But then, maybe 15 years ago or so, I started hitting
| use-cases that it was designed for and everything
| changed.
| seveibar wrote:
| XML is insanely powerful, there are 1,000 page books on
| XML alone. But React is a much smaller standard that
| extends XML to an insanely effective point (assuming
| basic JS as base knowledge) It's because I like XML that
| I think JSX is a logical/effective extension and we
| should support it as the sort of "champion" of
| declarative design
|
| Just to emphasize this: Why has XML been losing as the
| standard config format? Why did JSON win on web APIs?
| It's not because JSON is better, but because it's simpler
| and very-javascript-compatible.
|
| If you like XML, there is a strong argument to support
| JSX because it's a way to keep XML around!
| kristopolous wrote:
| XML continues to be a poor and inappropriate choice for
| simple stand-alone things.
|
| In APIs you're talking to 1 server, calling 1 function,
| that returns 1 kind of thing at 1ce.
|
| XML starts becoming useful when you increment those
| numbers - any of them.
|
| People (including me) have been trained not to think in
| those ways. The web still isn't a web - it's merely a
| collection of computers with individual relationships.
|
| The 90s internet had fleeting attempts at other
| paradigms. We should probably put serious effort into a
| second bite at that apple since by every measure our
| technology is at least 1000x better in the same way we've
| revisited neural networks
| treflop wrote:
| We're not talking about XML as a data transport format.
|
| As a way to ultimately render a view, it has never been
| elegant.
|
| I've done it every possible way:
|
| - Templating libraries
|
| - XHTML with embedded XML data
|
| - XML and XLST
|
| - XML embedded in JavaScript (EMCAScript), aka React
| before React existed
|
| - React, which is similar EMCAScript
|
| I used the first public releases of React. I was
| immediately sold. No one had to sell anything to me. I
| knew it was going to win.
|
| I can't imagine anyone with strong experience in all
| these ways possibly think that these other ways are more
| enjoyable.
|
| p.s. I'm not talking about Redux. I was never sold on
| Redux/Flux.
| bilater wrote:
| " People who are saying React only belongs on DOM are missing
| out"
|
| yup - two of my favorite React packages are Remotion and React
| Email.
| result2vino wrote:
| I'm sitting here waiting for you to close the sale on me. Who
| said anything about XML as the alternative!?
| wyager wrote:
| > the most robust type-system in the world (Typescript)
|
| The rare combo of Blub Paradox + Poe's Law
| mardifoufs wrote:
| What would you consider to be more robust, while still being
| usable? I'm referring to the type system here specifically.
| FridgeSeal wrote:
| Rudy, Haskell, Ocaml are obvious examples.
| timeon wrote:
| Anything with proper algebraic data types.
| ryukoposting wrote:
| > React combines the most robust type-system in the world
| (Typescript)
|
| By what measure?
|
| Your comment reads like you have something to sell me.
| consteval wrote:
| Right. It's a structural type system that automagically puns
| things at runtime. The "most robust type system", I would
| think, would be something like Haskell.
| com2kid wrote:
| > To give a sense of why this is important: Imagine a CLI built
| with XML.
|
| If I'm not mistaken, JSX is a type of XML blended with JS code.
| sureIy wrote:
| It's definitely XML/HTML inspired, but not _a type of XML_
| since it uses prop={value}
| timeon wrote:
| > most robust type-system in the world (Typescript)
|
| Are you serious?
| taskylizard wrote:
| See also the blog post by the author:
| https://vadimdemedes.com/posts/moving-on-from-ink
| magicmicah85 wrote:
| If your use case is mainly consuming web APIs or providing helper
| tools around your web framework, I can see why using this would
| be useful as you could reuse the same libraries you use in your
| application.
| d4mi3n wrote:
| I don't get all the hate. Sure, JS isn't the most popular
| language for TUI apps, but there's always room for more TUI tools
| and I'd you're automating things for your JS project this is a
| pretty natural fit to make your project scripts friendlier to
| their users.
|
| I think I'd much rather have more nice TUIs than not, regardless
| of the language they're written in.
| insane_dreamer wrote:
| Nice accomplishment! However, React seems overly complex for a
| TUI, unless you already work with React in the browser and love
| JS?
| klysm wrote:
| Obviously that depends on the complexity of the TUI. React is
| the best tool I've used to build UIs out of all the tools I've
| ever tried. I don't love JS at all.
| bcherny wrote:
| Ink is awesome, but it does have some rough edges. For example,
| want to absolutely position something on screen? Not possible.
| The UI also tends to flicker when you're doing anything
| complicated, in a way that can be hard to debug.
| oscb wrote:
| I like Ink as an idea for how to use React creatively. It's very
| well done. At the same I think most CLIs do not need this kind of
| interaction. It is better to keep them simple and verbose rather
| than fancy and obscured. I spent a long time removing Ink from an
| enterprise tool where it didn't fit as it was meant more for
| CI/CD. Not this tools fault of course.
| hinkley wrote:
| Half the tools I write end up being used in CI/CD so even if I
| do fancy in a terminal you have to do simple for non
| interactive shells.
| itronitron wrote:
| I'm curious how developers that use React, but have never written
| an application using the MVC paradigm, would describe React.
| klysm wrote:
| I've used both. MVC is too limiting
| cyberax wrote:
| React is nice, but it has some sharp edges.
|
| Basically, you're logically re-rendering _everything_ on every
| render. The React engine then diffs it with the last state, and
| then applies necessary actions to reconcile them. As a
| consequence, your rendering code is invoked by React, and you
| have to follow some rules to make sure that React doesn't have
| to _physically_ re-render everything.
|
| Another consequence is that physical component creation is
| taken out of your hands.
|
| This works great, if you're doing something with tons of simple
| components.
|
| It works less great if the actual rendering is a complicated
| task. E.g. you're making a map widget, or a 3D editor. You
| might be better off not using the reactive approach, and fall
| back onto the classic MVC.
| synergy20 wrote:
| very nice, write some js/ts with React and use node to run them
| to get a CLI.
| evantbyrne wrote:
| Looks like someone reported a dependency vulnerability back in
| June and never got a response. Seems like one of those projects
| that is a pretty neat experiment, but users should probably
| expect to maintain their own fork if they plan to use it.
| revskill wrote:
| React is a kind of declarative programming, not just for the UI
| !!!!
|
| I could take React as a kind of FP design pattern.
| adamscybot wrote:
| Odd to see this come up. I used it about 6 years ago to produce a
| CLI for devs. It worked very well and React concepts map cleanly
| to pretty much any "UI" target as said targets can also be well
| represented by declarative & composable trees.
|
| Probably, other Ui frameworks could also map, seeing as the
| declarative/composable tree pattern is ubiquitous now. So it's
| important to note it's _that_ pattern which enables this, and not
| a specific framework.
|
| But React is one of the ones that is also more removed from the
| web target than others.
___________________________________________________________________
(page generated 2024-11-01 23:00 UTC)