[HN Gopher] Rhombus: Macro-extensible language with conventional...
___________________________________________________________________
Rhombus: Macro-extensible language with conventional syntax built
on Racket
Author : networked
Score : 97 points
Date : 2024-08-04 06:11 UTC (16 hours ago)
(HTM) web link (docs.racket-lang.org)
(TXT) w3m dump (docs.racket-lang.org)
| TwentyPosts wrote:
| I feel like a link to a 'pitch' or description of the Rhombus
| language would've been more insightful for most of us. Even being
| familiar with Racket, I don't know what Rhombus is.
| metadat wrote:
| Agreed.
|
| Here's the link I found more informative:
|
| https://docs.racket-lang.org/rhombus/index.html
| adastra22 wrote:
| I didn't. Still not a single explanation of what the language
| is / what its designers are trying to do.
| philipswood wrote:
| Also not sure. I'm guessing it's some kind of Lisp
| bracketectomy - i.e. Lisp without the parentheses.
| Tuna-Fish wrote:
| Yes. It's a bracketectomy on Racket that's designed to
| (unlike previous bracketectomies) retain all the
| metaprogramming goodness of Lisp.
| dualogy wrote:
| Parens-less(er) Lisp, whitespace/indentation-based
| s-expressions.
| BoingBoomTschak wrote:
| Looks like "yet another Lisp without s-exprs for babby
| ducks". I'd probably go the Julia route if this is what I
| wanted, personally.
| adastra22 wrote:
| You are being down-voted for the snark, but there is
| point there. This seems to be "people use Python because
| of the whitespace, so let's remove parenthesis." Which if
| true, is (1) vastly missing the point, and (2) this
| project isn't really exploring any fundamentally new
| areas of language design then.
|
| I was kinda hoping to see this was the next generation of
| Lisp language design, but it appears to be just a
| different syntax.
| fgh wrote:
| The OOPSLA paper is more interesting:
| https://dl.acm.org/doi/pdf/10.1145/3622818
|
| Abstract: Rhombus is a new language that is built on
| Racket. It offers the same kind of language extensibility
| as Racket itself, but using conventional (infix) notation.
| Although Rhombus is far from the first language to support
| Lisp-style macros without Lisp-style parentheses, Rhombus
| offers a novel synthesis of macro technology that is
| practical and expressive. A key element is the use of
| multiple binding spaces for context-specific sublanguages.
| For example, expressions and pattern-matching forms can use
| the same operators with different meanings and without
| creating conflicts. Context-sensitive bindings, in turn,
| facilitate a language design that reduces the notational
| distance between the core language and macro facilities.
| For example, repetitions can be defined and used in binding
| and expression contexts generally, which enables a smoother
| transition from programming to metaprogramming. Finally,
| since handling static information (such as types) is also a
| necessary part of growing macros beyond Lisp, Rhombus
| includes support in its expansion protocol for
| communicating static information among bindings and
| expressions. The Rhombus implementation demonstrates that
| all of these pieces can work together in a coherent and
| user-friendly language.
| mkl wrote:
| This says more of that sort of thing: https://github.com/ra
| cket/rhombus/blob/master/resources/plan...
| stodor89 wrote:
| Is that the "Racket 2" thing they wanted to make some time
| ago?
| pas wrote:
| Yes
| int0x29 wrote:
| It would also help to have such a description in the README
| noelwelsh wrote:
| The pitch, compared to Racket, is conventional yet still
| extensible syntax.
| xscott wrote:
| I'll have to read about why they made the choices they made,
| but it reminds me of going to a microbrewery: You tell them
| you like simple (American) macrobrews, and they say they have
| just the thing. What arrives is a light lager that tastes
| awful because they think macrobrews beers are awful. So they
| made an awful beer that doesn't taste like a macrobrew.
|
| I don't think Rhombus is going to appeal to people that are
| comfortable with Python, C, Java, Go, JavaScript, etc... It's
| got infix, but it's cryptic. It seems like a step back from
| StandardML or Ocaml.
|
| Maybe there's a justification in terms of how the macros
| work. Maybe they're poisoning the well to make Lisp syntax
| more appealing. I'll try to reserve judgment, but so far I'd
| rather program in Scheme/Racket than this.
| pas wrote:
| As someone who gets the shakes from looking at too many
| parenthesis I think it's a great step in the right
| direction.
|
| A few weeks ago I went through the "GUI demo" source, and
| it's not bad. Of course just reading it doesn't tell much
| about what's the IDE support, how easy it is to figure out
| the arguments/types. (But it's rhombus/static, which is
| encouraging!)
|
| https://github.com/racket/rhombus/blob/master/gui_demo.rhm
| noelwelsh wrote:
| I don't think the Racket/Rhombus developers are really
| trying for adoption. They're trying to push the field
| forward via their research. Creating these useful
| programming languages is how they validate their research,
| but the end goal is not to grab a large share of working
| developers but to grab mindshare of the few developers who
| create the future of programming. In this they have been
| quite successful.
| xscott wrote:
| I agree they probably aren't going for world domination,
| but here is a nice video from Mathew Flatt where he says,
| "The point of Rhombus is to make Racket macro technology
| more accessible" and "removing an obstacle for most
| people":
|
| https://youtu.be/OLgEL4esYU0
|
| Having traditional infix operators, function calls, array
| subscript, and field access is a great start, but after
| that it doesn't look very traditional or familiar at all.
|
| I hope they succeed, and I'll keep following their
| progress, but when I look at their if-statements, it's
| pretty non-traditional: if
| is_rotten(apple) | get_another() |
| take_bite() be_happy()
|
| It looks like it has indentation-based grouping with
| vertical bars, white space, and colons implying different
| precedence, and that makes me have a lot more questions.
| I think my American macrobrew analogy above holds.
|
| https://docs.racket-lang.org/rhombus/Notation.html
| csande17 wrote:
| Yeah, the if statements are definitely a little weird-
| looking to me. It seems like the goal is to allow for
| more traditional infix syntax while still defining most
| of the language using the macro system; it seems
| unfortunate if their macro system can't handle more
| traditional-looking "if (x} block; else block"
| conditional statements.
| gus_massa wrote:
| a.k.a. Python-like with macros
| anonzzzies wrote:
| For people, like me, who don't know Rhombus: https://docs.racket-
| lang.org/rhombus/index.html
|
| Cannot see if/how this would win souls over Lisp-y syntax, but
| I'm a fan of the latter over most, so I'm probably very wrong.
| cocok wrote:
| I stopped at 1.1 Notation. Full of arbitrary-looking decisions
| on which characters can be used for what.
|
| It's 2024, and we still don't have a string notation that
| doesn't use the same character for opening and closing
| delimiter. If I start parsing from an arbitrary offset in the
| code, I can't say whether a double quote I read is the
| beginning of a string, or the end of one. I have to either
| resort to heuristics, or parse from the beginning of the file
| (at least once; and then cache offsets known to be outside a
| string). Something like "() would be nice. Still the familiar
| double quote, but the grouping is defined in a grammatically-
| superior way.
|
| Also, still no identifiers that can start with a digit. Most of
| the mainstream languages have such complex grammars, probably
| requiring hand-coded parsers, but I can't have a "52cards"
| identifier. Is this really that hard compared to everything
| else?
|
| Now, I'm self-taught and all. Maybe I'm missing something and
| the professors are right.
| ansible wrote:
| > _If I start parsing from an arbitrary offset in the code,
| ..._
|
| Why would you ever do that? What's the point?
|
| There are many other examples, but in C and C++, if you don't
| start parsing at the beginning, you're definitely going to
| get many things wrong. What if you start parsing in the
| middle of an identifier? How can you possibly expect to get
| something useful from that?
| HexDecOctBin wrote:
| > Why would you ever do that? What's the point?
|
| Syntax highlighting code visible on the screen
| ansible wrote:
| > _Syntax highlighting code visible on the screen_
|
| That... does not work in most programming languages.
| Especially so for showing indent levels correctly.
| Generally speaking, the language server (or whatever) is
| parsing the entire file.
| cocok wrote:
| > Why would you ever do that? What's the point?
|
| Performance, mostly. If you have to re-parse part of the
| code many times a second, in a text editor. For
| [pseudo]structural editing or syntax highlighting, for
| example.
| pas wrote:
| Cache the position of quotes/apostrophes, etc.
|
| Optimizing for your edge case would require everyone
| writing and reading code to conform to this extra thing,
| which seems completely unnecessary. Machines are pretty
| fast.
|
| https://www.youtube.com/watch?app=desktop&v=ZI198eFghJk
| Modernizing Compiler Design for Carbon Toolchain -
| Chandler Carruth - CppNow 2023
| lawn wrote:
| Elixir has a ~s"" syntax that you might like.
|
| Still, you're making a big deal of something very minor IMHO.
| 082349872349872 wrote:
| Forth almost has you covered on both points: `52cards`, `1+`,
| and `0` would all first be looked up in the dictionary, and
| only if they had not been defined would an integer conversion
| be attempted, and `s" ` is in principle distinct from the
| trailing `"`.
|
| [unicode does have various quotation-mark pairs, but note
| which is the opener can be natural-language dependent: eg <<
| french >> vs >>magazine german<<]
| Y_Y wrote:
| There is a classic English-language solution in the so
| called 66round quotes99, like the accursed SmartQuotes MS
| Word feature inflicts. There is more than one pair, and
| they look visually indistinct in some situations so I used
| superscript numbers above, rather than "this".
|
| I like the <<guillemot>> (French) solution, and also the
| similar <chevrons>. Like all similar holy wars there are
| issues with familiarity and input methods etc. and I don't
| think it will be solved through mere elegance.
|
| Honorable mention to the classic ``unix quotes'' that are
| still seen in typesetting software.
| cultofmetatron wrote:
| thankyou! its crazy to me that even the README for this project
| had pretty much nothing on what the project actually is.
| pas wrote:
| for anyone interested, I recommend spending a few minutes
| with this to get a feel for the language (and if something is
| undecipherable then searching the docs for that thing,
| because the docs are super verbose and hard to navigate, and
| too dry, IMHO):
|
| https://github.com/racket/rhombus/blob/master/gui_demo.rhm
| smokel wrote:
| Rhombus appears to be a relatively new language, built in the
| Racket ecosystem, with similar capabilities such as macro
| expansion.
|
| Racket uses a Lisp-style syntax, and Rhombus uses a more Python-
| like style that could attract a wider audience.
| luckymate wrote:
| What's the gain from using that over just using python?
| anonzzzies wrote:
| I don't think Rhombus is python style. And this won't convert
| anyone anyway; it's experimenting with new language
| constructs and such which is what racket is used for by most
| who use it. In many years from now, the lesson learned by
| these experiments might end up in a new language that might
| get converts and even rival python/js (I hope so, I find both
| terrible).
| kamaal wrote:
| I was skeptical about Rhombus in the past. But as it turns
| out making new programming languages is the whole point
| behind Racket.
|
| This not supposed to be a production use language, think of
| it more like used to teach, and experiment with programming
| language features.
|
| To that end, it will be used by its target audience(i.e,
| teachers and researchers)
| 1attice wrote:
| A lot of folks (including me) find Python limiting for non-
| trivial use cases. The One Python Way was a great selling
| point in 2004, but the thing is, _it 's still basically the
| same Way_ in 2024. So, I hope you like nerfed lambdas and
| inheritance
| 1attice wrote:
| Expanding on this point (because it's important):
|
| A lot of folks (including me) find Python limiting for non-
| trivial use cases. The One Python Way was a great selling
| point in 2004, but the thing is, _it 's still basically the
| same Way_ in 2024, and the fact that the language has been
| designed so exhaustively to ensure that there is only one
| correct way to do stuff, there has never been room for
| evolution. (Insufficient entropy!)
|
| I'm going to compare it to JavaScript/TypeScript, because
| it's what I know best in 2024, and because it's an engaging
| contrast; yet the take-home message is also applicable to
| other languages, such as Rhombus (which looks cool!)
|
| Python feels timeless to me, like Roman majuscules. It was,
| in its day, brilliant: cleaner than Java, saner than Perl,
| and just so, so hackable. The strong Pythonic cultural
| rejection of Perl's 'more than one way to do it' dictum was
| powerfully clarificatory; we didn't have StackOverflow, and
| the _good_ technical resources were still all in physical
| books, so being able to learn _one_ pretty-good way of
| expressing a concept or pattern was magical.
|
| But, like roman majusucles, Python _didn 't_ evolve,
| because it _didn 't have to_. The marginal cost of change
| threatened the original value proposition, so it just
| didn't really ever happen.
|
| By contrast, while e.g. JavaScript _had_ to evolve, because
| it was gobsmackingly bad, the _necessity_ of that evolution
| has made made JavaScript (as a community and language) open
| to variation, change, competing paradigms, and imports from
| academe and research. Evolution loves a mess.
|
| TypeScript, for example, happened nearly overnight, and as
| a result of it and other innovations, I can spend my day
| working blissfully in algebraic types, doing frigging set
| theory to do precise type hinting, and passing around pure
| functions and immutable structures. My code runs
| everywhere, and my coding style is always changing
| (improving!), and the only real price I've had to pay is
| learning some regrettable nonsense about an extra '=' in my
| comparison operators, and maybe the idiocy of having both
| `undefined` _and_ `null` types.
|
| Whereas, when I peep the pythonista channels at my work, I
| notice they are still having essentially the same
| conversation about eliminating the GIL that I remember them
| having in 2007 (yes I am old.)
|
| Which is not to say that Python is _bad_, per se; there are
| obvious advantages to having an imperfect but opinionated
| lingua franca, and I'd sure rather be thrown into an
| unfamiliar Python codebase from 10 years ago than an
| unfamiliar JavaScript codebase of an equivalent age.
|
| Yet I'll warrant that Python's long summer of success,
| combined with its one-way-to-do-it culture, close the mind
| and the imagination, and will eventually make it less fit-
| for-purpose than its competition. It will remain in use,
| and it will even find new contexts (machine learning, say)
| but 'the code part of the codebase' will be done in other
| languages.
|
| I suspect Python will, thanks to its exceptional
| readability and regularity, become a configuration language
| --- a part of the UI, essentially, a sort of shell. It will
| also continue to be a language used to teach programming.
| Hanging on here and there, sort of like how Latin hangs
| around in biology and medicine. But legacy Python
| codebases, thanks to that very readability, will probably
| be rewritten sooner rather than later.
|
| Standards (Latin, Python) are _useful_, and _timeless_
| standards are some of the most valuable artifacts humans
| have ever produced.
|
| But it's the Innovator's Dilemma for sure.
| smokel wrote:
| Hm, I don't think the "one way of doing things" koan
| holds up in practice, other than being a nice narrative.
|
| Nearly all other languages strive for doing things in one
| way, it is not something that makes Python unique. In
| fact, Python typically offers a complete mess of ways in
| which to solve something. Classes are sometimes good,
| sometimes they're not. Lists or Numpy arrays or Torch
| tensors, the choice depends mostly on performance, not on
| style.
|
| And Python _is_ evolving. There is optional type checking
| for instance.
| mkesper wrote:
| The 'one way of doing things' mantra must be seen against
| the crazy ways of Perl (and later Ruby).
| 1attice wrote:
| Just so. Code written in Python has always had the virtue
| of being _incredibly boring_, which is a virtue that, at
| the time of its inception, was criminally undervalued;
| this was, after all, the heyday of C++, and if you
| weren't bringing operator overloading and multiple
| inheritance and generics to the table, the hipsters
| sniffed.
|
| For example, no one complained that Python had multiple
| inheritance; instead, we thought this was _a point in its
| favour_, over and against Java. (I imagine Guido added it
| grudgingly as a vox-populi.)
|
| Thus, the Pythonic mindset emerged as a sort of 'refusal
| of the call', sort of like Indiana Jones shooting the
| sword guy (https://www.youtube.com/watch?v=kQKrmDLvijo).
| You could be against the hermetic complexity of Perl, but
| do it better than Java! Neat!
|
| These days, however, I suspect that Python, while still
| boring, is boring in the _wrong_ way, leaving
| opportunities for concision, clarity and performance on
| the table -- now-basic stuff like immutable datatypes,
| monads, tail recursion, concatenative programming, and so
| on.
|
| Python is the hobbit that stayed in Hobbiton.
| umanwizard wrote:
| I can't tell what you mean concretely, because the two
| examples you give -- gradual typing and lack of
| concurrently executing threads -- are common to both
| languages. Python has support for gradual typing, and
| JavaScript is single-threaded (which is an even stronger
| property than having a GIL).
| 1attice wrote:
| As I said, JavaScript was an example, picked because I
| work it it every day.
|
| There are most certainly better examples, that I am less
| equipped to present; Rust springs to mind.
|
| Yet whatever example is chosen, the claim remains
| unchanged: Python experiences the Innovator's Dilemma.
| frou_dh wrote:
| Python arguably has better support for working with sum-
| types (algebraic types) than TypeScript does, because the
| language actually has a `match` statement (since 2021).
| Define a sum-type as a Union of dataclasses and the
| static type-checker (Pyright) can even tell you when your
| pattern-matching statements are non-exhaustive.
|
| Do you track such developments, or spend the time
| dreaming up these elaborate theories? :)
| 1attice wrote:
| Not particularly (point taken), but I _do_ note that
| describing a napkin-sketch as an 'elaborate theory' is
| perhaps more flattering than you mean it to be ;)
| behnamoh wrote:
| > python feels timeless
|
| As a longtime python programmer I disagree. Python is so
| stuck in its old ways that several useful and interesting
| PEPs just get rejected. In 2024, the thought of not
| having macros, a decent version manager, not being able
| to modify running code despite being an interpreted
| language, not having multi line lambdas, not having
| several of core language features in lambdas (e.g., no
| try/except), the pain of creating thunks, the overhead of
| closures, not being able to read module files easily (if
| they're in another directory), etc. make Python one of
| the most frustrating languages.
| fire_lake wrote:
| I totally agree but always find myself in a very small
| minority when expressing this. People _love_ Python and I
| don't understand it.
|
| For me the issue is that one cannot write in a "light
| functional programming" style in Python. The language lacks
| many simple things, but chief of them is multi-line
| lambdas! In 2024 I'm very surprised that people aren't
| clamouring for this.
| BaculumMeumEst wrote:
| Ignoring the social benefits (which is easily the biggest
| draw of the language), Python feels really optimal for
| quick, simple tasks. The language lends itself to not
| overthinking it and building simple solutions.
| vintermann wrote:
| Macros, ease of making internal DSLs a la Haskell?
| OriPekelman wrote:
| Actually looks quite interesting. syntax looks like:
| fun | is_sorted([] || [_]): #true | is_sorted([head,
| next, tail, ...]): head .<= next && is_sorted([next,
| tail, ...]) is_sorted([1, 2, 3, 4, 5])
| is_sorted([1, 2, 30, 4, 5])
|
| a bunch of ways to express blocks, pattern matching and macros.
| The class facilities also look very nice. other than some
| indentation it doesn't feel pythony at all (which for me is a
| good thing!) more like an Elixir feel (cute syntax for some great
| concepts behind) see
| https://github.com/racket/rhombus/blob/master/demo.rhm
| adastra22 wrote:
| Looks like Haskell.
| trealira wrote:
| Or like Standard ML.
|
| But it's interesting, since I hadn't seen that part yet, only
| the loop part and some of the dynamic typing.
| for List: each i: [1, 2] each j: ["a", "b",
| "c"] [i, j] > [[1, "a"], [1, "b"], [1,
| "c"], [2, "a"], [2, "b"], [2, "c"]] for (i: 1..4):
| "number " +& i ~into List > ["number 1",
| "number 2", "number 3"] for: each:
| friend: ["Alice", "Bob", "Carol"] index: 1..4
| println(index +& ". " +& friend) 1. Alice 2. Bob
| 3. Carol
|
| It gave me the impression of more of a cross between Scheme
| and Python.
| aeonik wrote:
| Yes, I also got Python vibes. That behind said, I'm digging
| infix less and less over time, and I'm actually starting to
| crave forth syntax, just got the complete lack of
| punctuation...
|
| but, I actually really like what they have going here.
| Seems nice and minimalist, while meeting consistent and
| clean.
| scotty79 wrote:
| For a person that doesn't know racket, Haskell or any ML it
| looks unnecessarily convoluted.
|
| My most charitable take is that it solves some problems I never
| seen a computer language have.
| presentation wrote:
| As someone who has it doesn't look convoluted and solves
| problems I've seen many computer languages have, especially
| non-functional-style ones.
| scotty79 wrote:
| What does | solve?
|
| They are also using : ; , and a new line. Also ' and << >>
| that I have no idea how to type.
|
| They are using both :~ and :: for type hints.
|
| In none of the languages I've seen that was necessary or
| useful.
|
| I think the problem they were solving was ... we really
| want to be able to cram everything into one line, so even
| if you split lines you might just as well still use the
| same characters despite them being unnecessary then.
| soegaard wrote:
| The syntax `expr :~ annot` is used to annotate the
| expression with static information. This is different
| from type annotations.
|
| It's a general mechanism to associate an expression with
| "extra information" that can be used elsewhere (at
| compile time).
|
| One can for example using static information to implement
| dot notation for an object system. Using static
| information, one can push name resolution from runtime to
| compile time.
|
| The important part is that users of Rhombus can use the
| mechanism for tracking static information specific for
| their programs.
|
| It will be exciting to see the creative uses of this
| mechanism in the future.
|
| https://docs.racket-lang.org/rhombus/static-info.html
| scotty79 wrote:
| > The syntax `expr :~ annot` is used to annotate the
| expression with static information.
|
| Compiler can tell if a thing is static or dynamic and
| apply the correct behavior. Why would I ever want to
| check static thing only dynamically and why would I ever
| want to try statically check dynamic type if not by
| mistake?
|
| If programmer doesn't really have a real choice why make
| him choose which buttons to press?
| soegaard wrote:
| The idea behind macros is so to speak to allow the
| programmer to extend the compiler.
|
| In a language like MetaPost I can use equations between
| variables: x + y = 10 x - y = 6
|
| A reference to a variable x will use the current set of
| equations and solve for x. The solution (it it exists)
| will become the value of the variable reference.
|
| Let's say I want to extend Rhombus with this new kind of
| variable (let's call them linear variables).
|
| Each linear variable needs to have some static
| information attached. Here the relevant information is
| the set of equations to consider. A reference to a linear
| variable will then reduce (at compile time) the equations
| associated with the variable. The reduced set of
| equations is then used to generate runtime code that
| finds the value.
|
| In a sense "static information attached to an expression"
| is just a convenient way of working with compile time
| information (in the sense currently used by macros in
| Racket).
| vvillena wrote:
| The above is a super concise syntax example that showcases
| multiple things at the same time. It is function definition
| mixed with pattern matching, with some advanced pattern
| matching features, like the '...' and '||' symbols. You can
| rewrite the example into the code below, if you find it to be
| more readable. fun is_sorted(list):
| match list: | []: #true | [_]: #true
| | [head, next, tail, ...]: head .<= next &&
| is_sorted([next, tail, ...])
|
| There's also a dot in `head .<= next`, because the syntax is
| sugar for `head.<=(next)`. Not sure why this is needed, but a
| quick read on the docs suggests this is done to enable static
| dispatch for some calls.
| scotty79 wrote:
| It doesn't look terrible but colon after "match list" is
| optional.
|
| And how would the last line look if the previous one didn't
| end in a colon?
| JTyQZSnP3cQGa8B wrote:
| For those who are familiar with the Racket environment, is it
| easy to create standalone executables like Go?
|
| The Python syntax with the power of Lisp could be a fun thing to
| use on a CI pipeline.
| noelwelsh wrote:
| Yes. See https://docs.racket-lang.org/raco/exe.html
| JTyQZSnP3cQGa8B wrote:
| Thanks. I did DevOps for a few years and always used Python
| instead of a mix of PowerShell and Bash, but an alternative
| to Python could be nice.
| soegaard wrote:
| Also, check out:
|
| https://docs.racket-lang.org/zuo/index.html
| arthurcolle wrote:
| I remember learning what a rhombus was in elementary school.
| Good, simpler times.
| BaculumMeumEst wrote:
| Dude totally. And now my 3 year old lights up when we draw
| rhombuses (rhombi?). Such a fun shape.
| soegaard wrote:
| The reference manual for Rhombus is here:
|
| https://docs.racket-lang.org/rhombus/index.html
|
| Note that Rhombus is still a WIP, so a guide (and tutorials) will
| appear later.
| pas wrote:
| And here's a non-trivial example of how it looks/feels.
|
| https://github.com/racket/rhombus/blob/master/gui_demo.rhm
| dang wrote:
| All: we've changed the URL from
| https://github.com/racket/rhombus/discussions/521 to the base
| link suggested by commenters. Thanks!
|
| Also related:
|
| _Rhombus-in-the-Rough_ -
| https://news.ycombinator.com/item?id=36075516 - May 2023 (1
| comment)
|
| _State of Rhombus (programming language)_ -
| https://news.ycombinator.com/item?id=30314109 - Feb 2022 (17
| comments)
___________________________________________________________________
(page generated 2024-08-04 23:01 UTC)