Hacker News new | past | comments | ask | show | jobs | submit login
Compiling a Go program into a native binary for Nintendo Switch (ebiten.org)
361 points by hajimehoshi 4 months ago | hide | past | favorite | 42 comments



Hajime Hoshi is a true gem and the Go ecosystem is incredibly lucky to have him.

He was very approachable and friendly when I was toying with game development in Go.

I should get back to finishing that game in Ebiten. The tooling alone puts a smile on my face.


Which tooling in particular?


I'm guessing they're referring to Ebiten, a 2D library for game programming in Go: https://ebiten.org/


I absolutely love ebiten, it's gotten me back into game development which was why I initially got into software dev. I've been working through the Game Programming Patterns book and it's been an awesome platform to learn some of the common patterns in game dev. Also the maintainer is incredibly active and constantly pushing the development forward. I'm currently working on a side-scroller and am really excited to see if I can get it running on the switch.


Just came here to say that I also love Ebiten. It's so simple. It stays out of your way, and it has a ton of great examples. One of my goals for this year is to finally finish a game (after talking about it for over a decade). Ebiten is going to help me accomplish that goal.


Anything else you can recommend for starting out with game dev and appropriate algorithms? Is C++ a must?


There are game libraries for many languages, so for beginners to game development, anything will do. Small libraries abstract a few functionalities over the lower level ones (sprite batching, input/events handling, etc.) and handle multiplatform. Larger libraries may take care of other aspects (game loop, scene graph, etc.) and specialize on certain game types (3D, platform, RPG, etc.).

C++ is probably a must if you one writes large games and/or performance critical ones (regarding "high performance"/low level languages, Rust libraries are immature).

Scripting languages (e.g. Python/PyGame) can be a good (quick) way to start and experiment. But readability falls very quickly when complexity grows (e.g. difficult to interpret inheritance tree), so at some point one may easily feel the need to switch to a statically typed language.

Regarding algorithms, a very common beginners book is "Game Programming Patterns", which is also available for free in HTML: https://gameprogrammingpatterns.com/contents.html.


So, right now Unity, Cryengine and Godot all use C# as a language (though it's fairly new to Godot, but is fully supported). Godot is a free game engine to get started on and publish on to start with side projects/build up a portfolio on - though (and double check the documentation) you can use Unity for free and then they take a cut if your game makes more than X amount I THINK.


C and C++ are the lingua franca for the games industry, followed by C# for tooling, scripting and lightweight game engines. Some Assembly also helps for those last mile optimization tricks.

Naturally if you aren't looking into getting into the industry, rather having fun at coding games, anything that can do graphics and sound will do.


Ebiten is actually making me want to get back into Go itself. I'd love to make a cross platform game and it may just motivate me enough to push through my ideological biases against Go.


I've yet to meet an ideological bias against Go that took into account & understood the practical tradeoffs Go is making. The ones I've seen thus far are dismissive and incurious. But if you've got that, I would love to hear it.


I write golang, and am a big fan, however it lacked/lacks some features that i would expect in a modern language.

- Lack of generics(coming very soon) was a huge problem for a long time and resulted in masses of duplicated code, and shonky reimplememtations rather than battle tested reusable implementations.

- Lack of sum types, which is particularly frustrating with go's error handling model (which I happen to be a fan of, mostly). The state of a return value depends on another return value in many cases. There is nothing in the language stopping me from using the value returned from a function that has returned an error. Even c++ has a (half baked) solution for this with optional.

- go's error handling has a major issue; it forces my model to be able to represent invalid state (see above). Because go doesn't let me have a sum type, I'm forced to return a "valid" return value alongside the error value, which means that state has to be representing in my code.

- repetition. Contrary to best practices in every other programming language, the advice given for go on so is frequently "its not that much work to implement the X yourself". Now instead of using an existing well supported library I'm maintaining my own X.

- cgo. Performance is terrible, its completely unique compared to every other FFI style system (e.g. see dotnet), and it doesn't work with MSVC.

> that took into account & understood the practical tradeoffs Go is making.

Just because the language had made decisions because of tradeoffs doesn't make it immune from criticism. If it's OK to criticise c++ for not breaking backwards compatibility (vector<bool> comes to mind), it's OK to criticise go for its shortcomings.


>Just because the language had made decisions because of tradeoffs doesn't make it immune from criticism. If it's OK to criticise c++ for not breaking backwards compatibility (vector<bool> comes to mind), it's OK to criticise go for its shortcomings.

I never said it was immune to criticism. I think most of the criticism is misguided.

>- go's error handling has a major issue; it forces my model to be able to represent invalid state (see above). Because go doesn't let me have a sum type, I'm forced to return a "valid" return value alongside the error value, which means that state has to be representing in my code.

I recognize that this does not perfectly map to your mental model, but why is this a major issue? I think major issues are those that cause practical problems.

I also think there is a question of what the cost, in terms of developer cognitive load, would be of adding more advanced modeling features to go.

It's a bit like saying "that tire isn't perfect because it can pop, so lets use a metal tire instead". Sure, you fix one issue, but you create others.

>- repetition. Contrary to best practices in every other programming language, the advice given for go on so is frequently "its not that much work to implement the X yourself". Now instead of using an existing well supported library I'm maintaining my own X.

This is too vague to evaluate. There is a point at which repetition becomes a problem, and there is a point at which repetition is cheaper than having an abstraction. I don't think it can be evaluated in general.

> cgo. Performance is terrible, its completely unique compared to every other FFI style system (e.g. see dotnet), and it doesn't work with MSVC.

I agree on the factual level that it would be nice if cgo didn't have so much gc overhead. If this is meant to be evidence that go is a bad language, I would disagree.


> I also think there is a question of what the cost, in terms of developer cognitive load, would be of adding more advanced modeling features to go.

That's the thing about Sum Types. They're not really an advanced feature at all: they map to an extremely intuitive human concept: "or". They reduce cognitive load. That's the advantage of having them. In fact, I suspect that a lot of the reason that dynamic languages are considered simpler is because every variable is essentially one big sum type. Trying to represent "or data types" without sum types requires you to use interfaces which are a much more advanced language feature, and are very awkward for the use case of a finite number of known cases.

You could have a perfectly functional language with only logical AND (&&) and logical negation (!) and no logical OR (||). It wouldn't cause any practical issues. But you wouldn't: it would be incredibly awkward. Likewise there should be a datatype for OR (sum types) as well as AND (structs/classes).


This comment pretty much sums up the idea of Go — it's a perfect language for those who haven't looked into anything else that's been available for the past fifty years. If you think that sum types is an "advanced feature" that puts additional "cognitive overhead", it may be perfect for you. I think iota puts much more overhead and adds a need to have oversight where there should be none. If your idea of less cognitive overhead if doing compiler's work for it, well, good for you. There are some exceptions among talented and well-educated developers who seem like it, I know, but those seem to be exceptions.


> There are some exceptions among talented and well-educated developers who seem like it, I know, but those seem to be exceptions.

I have to disagree with this (but agree with the rest of your post). The issues with go are there, but it's still a robust language with great tooling, a solid underlying ecosystem, and is actively improving. It performs pretty well, and idiomatic go is mostly transferable across projects (as opposed to say c++ where you have to pick your favourite style guide). I think it's a great tool for developers, even given its shortcomings


I know Haskell, rust and lisp, so no, I have looked into and used these technologies in earnest. Taking the cost of complication for granted is naive.


I question the notion that sum types add complication. I would suggest that if teaching complete beginners it would be just as easy to teach them sum types as it is to teach them product types (structs/classes). A bunch of languages have classes with inheritance. Now that's complicated! But sum types? I just don't buy it.


Curious, could you elaborate how a dynamic languages variables are essentially sum types? Im not very familiar with types as a whole, though I've been doing some exploration in TS and Rust over the post few weeks


So a Sum Type is a type that can be one of many variants. In Rust this could be something like

     enum CustomType {
          String(String),
          Number(f64),
     }
(note: naming of String and Number is arbitrary here)

Where a variable of type CustomType can be either CustomType::String (in which case it contains a string) or CustomType::Number (in which case it contains a number (specifically a 64bit float)).

In typescript this same type would be:

     type CustomType = string | number;
A feature of sum types (in contrast to C/C++ "unions" which are an otherwise similar concept) is that they store which variant a current variable is using. So in Rust you can do:

    let foo_or_bar = CustomType::Bar(1);
    match foo_or_bar {
        CustomType::String(string) => { /* foo_or_bar is String */ },
        CustomType::Number(number) => { /* foo_or_bar is Number */ },
    }
And in TypeScript (and JavaScript!) you can do:

    let fooOrBar = 1;
    switch(typeof fooOrBar) {
        case "number":
             // fooOrBar is a number
             break;
        case "string":
             // fooOrBar is a string
             break;
Now notice that a JavaScript variable (or a variable of type `any` in typescript) is essentially the same as this except there are more options. If you were to model it in Rust you might have something like:

     enum JSValue {
          Null,
          Undefined,
          Boolean(bool),
          Number(f64),
          String(String),
          Array(Vec<JSValue>),
          Object(HashMap<String, JSValue>),
          Function(fn),
     }
With perhaps some more types to handle variants to handle less common types like big numbers, typed arrays, etc. And in JavaScript this is just the default type that applies to all variables! And as it applies to all variables you don't have to specify that a variable is of type JSValue, you just specify the inner type to be 7 or false or null or "foo" or whatever and it chooses the correct variant for you. But internally the JS engine is keeping track of what type your variable is, and it allows you to query it at runtime (although it may not be as simple as typeof).


Wow awesome explanation, that's so cool, thank you!!


Trying not to devlove into an ad hominem, I don't think you're arguing in good faith, You're dismissing my criticisims (which are valid critiques of golang and the ecosystem of it) by hand waving them away.

> I recognize that this does not perfectly map to your mental model, but why is this a major issue?

The phrasing of this implies that it's my fault that go forces me to represent invalid states, not the languages. Here [0] is a 30 line (contrived for brevity) example showing something that is just not possible if you use C++'s std::optional, or Rust's Result types.

> It's a bit like saying "that tire isn't perfect because it can pop, so lets use a metal tire instead". Sure, you fix one issue, but you create others.

Actually I think it's more like saying "A metal band around a wheel is fine, why would we wrap them in rubber, that just causes more issues".

> This is too vague to evaluate. There is a point at which repetition becomes a problem, and there is a point at which repetition is cheaper than having an abstraction. I don't think it can be evaluated in general.

I disagree. Fundamentally the js ecosystem suffers from being too far the opposite (left-pad), and golang swings too far in the repitition direction. Here's some examples of really fundamental things that are missing from the language/libraries, that the response is "just write your own" [1][2][3][4][5]

> If this is meant to be evidence that go is a bad language, I would disagree.

The method of actually interacting with cgo is completely bespoke and novel, and unlike any other FFI system, without any advantages for doing so. It's fair and valid to criticise C++ for it's arcane build system, lack of package manager, lack of abi stability, and name mangling. It's fair to criticise js for it's over reliance on tiny packages, npm and all the extra compile steps it requires for an uncompiled language. It's fair to criticise rust for excruciatingly long build times, therefore it's fair to criticise go for it's half baked slow ffi IMO.

[0] https://gcc.godbolt.org/z/eKWq6rnb7

[1] https://stackoverflow.com/questions/44940822

[2] https://stackoverflow.com/questions/21362950

[3] https://stackoverflow.com/questions/7782411 (no way to iterate through the container itself, only make a copy)

[4] https://stackoverflow.com/questions/12518876

[5] https://stackoverflow.com/questions/19979178


You seem to ask for "ideological bias" and then discount all the brought up examples as "impractical" at the same time.

For example, I may dislike a language simply because it has no significant white-space: I just like a programming language to display nicely, for my definition of "nicely". I do understand practical tradeoffs of significant whitespace, yet I still like it: those are not in opposition with each other.


Your definition of nicely and it’s origin can be examined. We can also question what it means for a programming language to be good or bad.


This thread started off with

> ...my ideological biases against Go

which you turned into a question of whether Go is a good or bad language. That was never brought up.

An idealogical bias against Go could be that it was conceived by Google, and that you reject to support increasing Google's influence on the world.

This has nothing to do with Go being technically good or bad.


Ok, sure, my terminology could have been more accurate, but I’ve been having perfectly functional conversations with everyone else, so I’m not sure what you’re getting at.


None of it seems to have been "functional conversations with everyone else", but instead you asking questions after "ideological biases" were brought up and then dismissing all the answers as irrelevant because they are not clearing any of your "practical" bars.

That was at least my impression, and I fully admit that I may have gotten it wrong (I have no skin in this game, I am just portraying what your "conversations" looked like from the sidelines).


To me, Go is just a boring language to write in. It’s target audience is large corporations where individuality is discouraged. I’ve used Go extensively at work, and its lack of expressivity definitely has some upsides on large teams, mainly keeping things simple and understandable. But when I program in my free time for fun, I want to be able to express myself fully.


I agree with you that Go is a boring language. I think in Go's boredom is a path to creating better code than you are pushed towards in other languages, and I enjoy writing Go in my free time because I enjoy learning about API design through interacting with the STL, and because I produce programs in it that I understand much more deeply (and with relative ease) than when I try to use another language. However, Go is not a good language for experimenting with cutting edge language features.


I actually mostly agree. I just don't really like the aesthetics of writing Go, which I know is a silly reason.


I'm not one to call aesthetics silly. The act of reading and writing programs relies heavily on the programmers emotion, and so I think aesthetics matter greatly. What don't you like about them?


I'll bite. I've read all the arguments the creators of golang made for not including generics. They are just as true now as they were when the language was created, and yet generics have now been added. Moreover, they have been added as part of golang 2.0, so any breaking changes that turned out to be needed could have been made now anyway. I think it's pretty reasonable to dislike a language that refuses to implement a basic feature for essentially no reason.


This seems rather clueless. The party line was they have not found a way to implement it in a way that satisfy their requirement. Now they have found a way to do so. So I see no contradiction. Also what breaking changes you are talking about I am not aware of any. Also there is no golang 2.0 yet.


> The party line was they have not found a way to implement it in a way that satisfy their requirement. Now they have found a way to do so.

So far as I'm aware, every mainstream language, including golang, that's addressed this question has chosen to solve it by monomorphising generic functions. This implementation was well understood when golang was first released, and it isn't remotely plausible that the authors were not aware of it.

> Also what breaking changes you are talking about I am not aware of any. Also there is no golang 2.0 yet.

An argument given at the time was that committing to an implementation of generics would potentially put golang in an awkward position similar to Java's wherein if the implementation turned out to be bad, the language would be stuck with it anyway for backward compatibility reasons. I am simply pointing out that, since both golang 2 and generics have been discussed roughly contemporaneously (generics landing shortly, golang 2 having first been discussed in 2019 and therefore presumably to happen at some point soonish), any initial implementation of generics would not have particularly constrained what the golang team is now doing.


It was a design goal that monomorphization should always be possible, but Go will not be monomorphizing generic functions to save on compile time / binary bloat (it will instead use GCShape stenciling, see below). I suspect future releases will be smarter and monomorphize some functions for performance, but that's really up to the Go team.

Initial implementation strategies considered for generics: https://github.com/golang/proposal/blob/master/design/generi... https://github.com/golang/proposal/blob/master/design/generi... https://github.com/golang/proposal/blob/master/design/generi...


There will be no Go 2 with breaking changes, it is just a marketing term for go with generics. I'm not fully clued in on the technical challenges with generics, but frankly you don't seem to be either. I know that there has been much ink spilled on the go-nuts mailing list and the issue tracker, as well as 3+ prior attempts at implementing generics, so I don't think it's fair to dismiss the go team's concerns out of hand.

Also, even if you were right and the go team simply said one day "we wont add generics lol" that would still be a very shallow reason to dislike the language. After all, that is only one of many aspects of the language, with a specific set of tradeoffs. The rest of the language can hold great value even if it doesn't look like the Hacker News Approved Language.


I'm not big on Go either but I feel like it's a good fit for hobbyist game development. It's a lot harder to write automated tests for games, so the value of static typing is higher, but most simple game designs don't really benefit from the powerful type systems of a Rust or Haskell (or even a TypeScript). It's one of the cases where Go actually does feel like a "better C".


> but most simple game designs don't really benefit from the powerful type systems of a Rust or Haskell (or even a TypeScript).

I get that feeling too but I have no experience with game development. I wonder what reasons you think so?


There is also some support for the Switch with TinyGo (A Go compiler for embedded system), but looking at the current state of the docs for this platform it's hard to tell how well supported it might be: https://tinygo.org/docs/reference/microcontrollers/nintendos...


Been using Ebiten for close to two years. One of, if not the nicest 2D engines I've used.

Nice to see Hoshi's work shared here.


This is awesome. Sad to see the work is kind of “wasted” in the sense that it’s not officially supported and any update to Go will likely break it. But I learned a lot reading this article.


It's apparent that the author intends to maintain this across Go version upgrades. Part of this work is a custom patch format to make it easier to manage the changes to the Go runtime (https://ebiten.org/blog/native_compiling_for_nintendo_switch...).

The author also explicitly stated that he did not expect major work to upgrade to a new Go version in https://ebiten.org/blog/native_compiling_for_nintendo_switch....




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: