Live data from Hacker News

Lisette a little language inspired by Rust that compiles to Go

lisette.run

161–168 of 168 posts

Re: Lisette a little language inspired by Rust that compiles to Go

#161
post #75

Earlier quoted context omitted.

Both Borgo and now Lisette seem to act as though (T, error) returns are equivalent to a Result sum type, but this is not semantically valid in all cases. The io.Reader interface's Read method, for example, specifies not only that (n!=0, io.EOF) is a valid return pattern, but moreover that it is not even an error condition, just a terminal condition. If you treat the two return values as mutually exclusive, you either…

You are right, and thank you for pointing this out. I've opened an issue: https://github.com/ivov/lisette/issues/12 I have a few approaches in mind and will be addressing this soon.

I gave Lisette a run today. I really like it, its a clear improvement to Go.

Here a few things that i noticed.

- Third party Go code support (like go-chi) is a absolute must have. This is THE feature that will possibly sky-rocket Lisette adoption. So something like stubs etc, maybe something like ReScript has for its JS interop (https://rescript-lang.org/docs/manual/external). The cli tool could probably infer and make these stubs semi-easily, as the go typesystem is kind of simple.

- The HM claim did confuse me. It does not infer when matching on an Enum, but i have to manually type the enum type to get the compiler to agree on what is being matched on. Note, this is a HARD problem (ocaml does this probably the best), and maybe outside the scope of Lisette, but maybe tweak the docs if this is the case. (eg. infers somethings, but not all things)

- Can this be adopted gradually? Meaning a part is Go code, and a part generated from Lisette. Something like Haxe perhaps. This ties to issue 1 (3rd party interop)

But so far this is the BEST compile to Go language, and you are onto something. This might get big if the main issues are resolved.

Re: Lisette a little language inspired by Rust that compiles to Go

#162

Earlier quoted context omitted.

To be fair, Go’s async story only works because there’s a prologue compiled into every single function that says “before I execute this function, should another goroutine run instead?” and you pay that cost on every function call. (Granted, that prologue is also used for other features like GC checks and stack size guards, but the point still stands.) Languages that aspire to having zero-cost abstractions can’t make…

I'm not sure this is 100% correct. I haven't researched it but why would they perform such a check at runtime if it is 1)material and 2) can be done at compile time. However, even if it is, Go is only trying to be medium fast / efficient in the same realm as its garbage collected peers (Java and C#). If you want to look at Rust peer languages though, I do think the direction the Zig team is heading with 0.16 looks li…

> why would they perform such a check at runtime if it is 1)material and 2) can be done at compile time

It can’t be done at compile time because it’s a scheduler. Goroutines are scheduled in userland, they map M:N to “real” threads, so something has to be able to say “this thread needs to switch to a different goroutine”.

There’s two ways of doing this:

- Signal-based preemption: Set an alarm (which requires a syscall) that will interrupt the thread after a timeout, transferring control to the goroutine scheduler

- Insert a check to see if a re-schedule needs to happen, in certain choice parts of the compiled code (ie. At function call entry points.)

Golang used to only do the second one (and you can go back to this behavior with - asyncpreemptoff=1), it’s why there was a well-known issue that if you entered an infinite loop in a goroutine and never called any functions, other goroutines would be starved. They fixed that by implementing signal-based preemption above too, but it’s done on top of the second approach.

Granted, the prologue needs to happen anyway, because go needs to check if the stack needs to grow, on every function call. So there’s basically a “hook” installed into this prologue that is a single branch, saying “if the scheduler needs to switch, jump there now”, and it basically works sort of like an atomic bool the scheduler writes to when it needs to re-schedule a goroutine… Setting it to true causes that function to jump to the scheduler.

Go has done a lot of work to make all of this fast, and you’re right that it only aspires to be a “medium-fast” language, and things like mandatory GC make these sort of prologues round to zero in the scheme of things. But it’s something other languages are fully within their rights to avoid, is my point (and it sounds like you agree.)

Re: Lisette a little language inspired by Rust that compiles to Go

#163
post #7

There are several languages that compile to Go, trying to be a better a Go. Off the top of my head: XGo ( https://github.com/goplus ), Borgo ( https://github.com/borgo-lang/borgo ), Soppo ( https://github.com/halcyonnouveau/soppo )...

Also: goeval https://github.com/dolmen-go/goeval

Re: Lisette a little language inspired by Rust that compiles to Go

#164

Earlier quoted context omitted.

I'm not sure this is 100% correct. I haven't researched it but why would they perform such a check at runtime if it is 1)material and 2) can be done at compile time. However, even if it is, Go is only trying to be medium fast / efficient in the same realm as its garbage collected peers (Java and C#). If you want to look at Rust peer languages though, I do think the direction the Zig team is heading with 0.16 looks li…

> why would they perform such a check at runtime if it is 1)material and 2) can be done at compile time It can’t be done at compile time because it’s a scheduler. Goroutines are scheduled in userland, they map M:N to “real” threads, so something has to be able to say “this thread needs to switch to a different goroutine”. There’s two ways of doing this: - Signal-based preemption: Set an alarm (which requires a syscal…

It sounds like you know about this / have researched it. Are you saying that any go function, even func add(x,y int) { return x + y}, is going to have such overhead in all situations? Why wouldn't Go just inline this for instance when it can? It seems like such an obvious optimization.

Re: Lisette a little language inspired by Rust that compiles to Go

#165

Earlier quoted context omitted.

> why would they perform such a check at runtime if it is 1)material and 2) can be done at compile time It can’t be done at compile time because it’s a scheduler. Goroutines are scheduled in userland, they map M:N to “real” threads, so something has to be able to say “this thread needs to switch to a different goroutine”. There’s two ways of doing this: - Signal-based preemption: Set an alarm (which requires a syscal…

It sounds like you know about this / have researched it. Are you saying that any go function, even func add(x,y int) { return x + y}, is going to have such overhead in all situations? Why wouldn't Go just inline this for instance when it can? It seems like such an obvious optimization.

If go chooses to inline a function in general, then it doesn’t need to add the prologue to the inlined code, no. The prologue applies to all functions that remain after the inlining is done.

There’s also functions that can be marked as “nosplit” that skip the prologue as well.

But otherwise, it has to be in every function because you might be 1 byte away from the top of go’s (small) stack size, then you call that simple add function, and if the prologue isn’t run the stack will overflow. Go has tiny stacks by default that grow if they need to, with this prologue functioning as the “do I need to split/grow the stack?” check, so it needs to be every function that does it. The scheduler hook is just a single branch that’s part of the prologue, so it’s not that much more expensive if you’re doing the prologue anyway.

Re: Lisette a little language inspired by Rust that compiles to Go

#166

Earlier quoted context omitted.

You are right, and thank you for pointing this out. I've opened an issue: https://github.com/ivov/lisette/issues/12 I have a few approaches in mind and will be addressing this soon.

I gave Lisette a run today. I really like it, its a clear improvement to Go. Here a few things that i noticed. - Third party Go code support (like go-chi) is a absolute must have. This is THE feature that will possibly sky-rocket Lisette adoption. So something like stubs etc, maybe something like ReScript has for its JS interop ( https://rescript-lang.org/docs/manual/external ). The cli tool could probably infer and…

Thanks for trying it out!

Variant qualification is a name resolution requirement - Lis follows Rust's scoping model where variants are namespaced under the enum. The implementation correctly infers the type, as shown e.g. in the hint `help: Use Shape.Circle to match this variant` My understanding is HM has nothing to say about this; it operates after names are resolved.

Re: Go third-party packages + incremental adoption, I'll do my best! Thanks for the encouragement.

Re: Lisette a little language inspired by Rust that compiles to Go

#167
post #79

Earlier quoted context omitted.

Your readme would really benefit from code snippets illustrating the library. The context it currently contains is valuable but it’s more what I’d expect at the bottom of the readme as something more like historical context for why you wrote it.

Yup, in my TODO list (I've only recently published this package). For now you can just check the tests, or a SO answer I wrote a while ago (before I published the idea as an npm package): https://stackoverflow.com/a/78937127/544947

I still struggle to see the advantage of Option in a language with null-safe accessors and unions

    function fnO(val?: number) {
        if (val == null) {
            return "NAH";
        } else {
            return (val * val).toString();
        }
    }

    test("testing Options", () => {
        let foo = undefined;
        let bar = 2;
        expect(fnO(foo)).toBe("NAH");
        expect(fnO(bar)).toBe("4");
    });
Your readme states

> FP's languages approach of rather not having null at all

But `None` is just another null / undefined, which brings along a bunch of non-idiomatic code around handling it.

Re: Lisette a little language inspired by Rust that compiles to Go

#168
post #79

Earlier quoted context omitted.

Yup, in my TODO list (I've only recently published this package). For now you can just check the tests, or a SO answer I wrote a while ago (before I published the idea as an npm package): https://stackoverflow.com/a/78937127/544947

I still struggle to see the advantage of Option in a language with null-safe accessors and unions function fnO(val?: number) { if (val == null) { return "NAH"; } else { return (val * val).toString(); } } test("testing Options", () => { let foo = undefined; let bar = 2; expect(fnO(foo)).toBe("NAH"); expect(fnO(bar)).toBe("4"); }); Your readme states > FP's languages approach of rather not having null at all But `None`…

None is not just another nullish value.

If you do a type check with None, and there is some value inside (so it is Some, not None), it is IMPOSSIBLE that the .value that you extract underneath is gone. This is an important race-condition that you might run into due to the nature of TS/JS, but by boxing the value with an immutable Option type, you're protected.

Also this prevents people to run into NullReferenceException (or UndefinedRefsExceptions, or whatever is called in this ecosystem) for people that didn't turn strictNullChecks ON.

Post reply on HN