HNHacker News
TopNewBestAskShowJobs

andolanra

1,874 karma · joined July 19, 2010

submissionscomments
andolanra··on Game Development Post-Unity
I'm not sure what "cool advanced projects" you're thinking of, but to my recollection, there was very little in the way of finished work created with the original Blender Game Engine. There were two game efforts associated with the Open Movie Projects: Yo Frankie! (associated with Big Buck Bunny) and Sintel The Game (associated with Sintel.) The former had something like a proper release, but the latter only ever got a few alpha releases before being abandoned: neither was terribly impressive except inasmuch as a demo of the technology, and to my recollection, neither inspired much hype. (The Wikipedia page for Yo Frankie! says dryly, "The game was noted by the gaming press," without any further elaboration, which does not speak to massive amounts of excitement or hype.)

Since then, aside from some interesting bits of work that didn't actually make shipping games easier, the engine really languished. My understanding is that a big part of the reason the engine was removed was that it made work on other parts of Blender more difficult, and that dispensing with the (little-used) engine in favor of the (increasingly popular) modeling tool was clearly a net benefit to being able to develop Blender, especially since the mantle of "open source game engine" had been taken up by other competent projects like Godot.

So yes, they may have at some point aspired to becoming a "mainstream game engine", but there's more to that effort than just aspirations and demos. (And at the same time, the fact that the BGE faltered doesn't mean that other open source game engine efforts would necessarily face the same fate.)

It's probably also worth saying that people have forked the game engine and you can still use it: the forked version is called UPBGE, and there are people out there trying to make it work. By and large, though, the Blender project seems to point people at projects like Godot, with the idea that a focused game engine is probably better suited to modern games than something that strapped a game engine onto a piece of modeling software.

andolanra··on Game Development Post-Unity
Nobody mentions the idea or recommends it anymore because the game engine was removed from Blender entirely back in 2019, in favor of purpose-build game engines like Godot: https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/R...
andolanra··on Leaving Haskell behind
The former is referring to representing an AST using a GADT in order to include the typing discipline of the target language in the host language. For example:

  data Term t where
    Num :: Integer -> Term Integer
    Bool :: Integer -> Term Integer
    Add :: Term Integer -> Term Integer -> Term Integer
    IsZero :: Term Integer -> Term Bool
    IfThenElse :: Term Bool -> Term a -> Term a -> Term a
With this AST, you can express well-typed programs like `Add (Num 2) (Num 3)`, but the Haskell type system will stop if you express an incorrectly-typed program like `Add (Num 2) (Bool False)`.

The "Trees That Grow" paper, on the other hand, is about reusing the same AST but gradually adding more information to the nodes as you progress through the compiler. For example, you might want to start with variable names being raw strings (so that a term corresponding to `lambda x: lambda x: x` looks like `Lam "x" (Lam "x" (Var "x"))`) but eventually replace them with unique symbols so that shadowed names are non-identical (so that under the hood it looks more like `Lam 1 (Lam 2 (Var 2))`, although in practice you'd want to keep the old name around somewhere for debugging.)

One way to accomplish this is to introduce an explicit type-level notion of compiler phases, give your terms a type parameter which corresponds to the phase, and use the phase to choose different representations for the same nodes:

  data CompilerPhase = Parsed | Resolved
  
  data Expr (phase :: CompilerPhase)
    = Lam (Name phase) (Expr phase)
    | App (Expr phase) (Expr phase)
    | Var (Name phase)

  type family Name (t :: CompilerPhase) :: *
  type instance Name Parsed = String
  type instance Name Resolved = Int
Using this example, an `Expr Parsed` will contain variables that are just strings, while an `Expr Resolved` will contain variables that are integers, and you can write a pass `resolve :: Expr Parsed -> Expr Resolved` which just modifies the AST. (This is a toy example: in a real compiler, you'd probably want to create a new type for resolved variables that still keeps a copy of the name around and maybe some location information that points to the place the variable was introduced.)
andolanra··on Ruby's hash is a Swiss-army knife
That's kind of the point of default values, though. If you wanted to make a change to the method and you want it to break to alert you to all the calls, then you can add a keyword without adding a default value for it.

    def foo(kwargs = {}, frob:)
      kwargs
    end
andolanra··on Ruby's hash is a Swiss-army knife
Since Ruby 3, the automatic coercion of keywords to a hash—the second example underneath "Passing Hash to Functions" in this post—is considered a legacy style and is generally frowned upon in new code. That is to say, code like the second call to `foo` here:

    def foo(kwargs = {})
      kwargs
    end
    
    foo({k: 1})  # ok: passing hash argument
    foo(k: 1)    # ok: keywords coerced to hash
One of the strongest arguments for avoiding this sugar is that it makes the code more brittle in the face of future changes. In particular, in the example above, if we add a new keyword argument to `foo`, then any call which omitted the curly braces will break, while calls which used them will keep working fine:

    # added a new keyword arg here
    def foo(kwargs = {}, frob: false)
      kwargs
    end
    
    foo({k: 1})  # still ok: `frob` defaults to false
    foo(k: 1)    # ArgumentError: no keyword: :k
This is touched on in the blog post describing the extensive changes made to keywords in Ruby 3: https://www.ruby-lang.org/en/news/2019/12/12/separation-of-p...
andolanra··on Tuple Space (2014)
If you know git: a federated wiki works like that. To make a federated wiki work like a traditional wiki, you can think of there as being a "canonical copy" which you can clone, make edits, and then use a 'pull request' process for incorporating those edits back into the original. But you also don't need to have a single source of truth: you can, for example, have a group of people—say, students studying for a class—who are building their own wikis, and among each other they can copy in pages, make changes, copy changes back, and so forth, all building a web of things. In the same way that git can replicate a subversion-like workflow but also introduces the possibility of different workflows, a federated wiki can replicate a traditional wiki but also has a number of workflows it can accomplish.
andolanra··on An Open Letter on the Open Gaming License, to Wizards of the Coast
The other one I mentioned—Maze Rats—is licensed as CC BY 4.0, so that might be a good place to start! (The Black Hack is licensed under the OGL 1.0, which unfortunately means it's not immune to the whole mess that this thread is about, but I'm hoping that a lot of the creators who put stuff out under the OGL 1.0 either come up with a new license that's not associated with WotC or relicense their stuff under something like a CC license. I guess we'll see!)
andolanra··on An Open Letter on the Open Gaming License, to Wizards of the Coast
It depends on what you're looking for! I'll give just a handful here, but I'm happy to expand if you have a specific follow-up questions to these.

If you're interesting in something very D&D-like, there's obviously Pathfinder 2E[1], which builds on the same D&D skeleton but has a much sharper and cleaner approach to grid-based tactical combat. However, I'd also suggest looking at some of the games in the OSR ("Old School Renaissance") space. Games like The Black Hack[2] or Maze Rats[3] provide a much smaller set of rules which are easy to adapt to other adventures: the goal is that you can take adventure modules for effectively any existing D&D-like game—including both present and past versions of D&D—and run them with little overhead.

Something pretty different mechanically but which is quite compelling in that space is Torchbearer[4], which is based on the underlying Burning Wheel[5] system but made significantly simpler (and shares a lot of those simplifications with Mouse Guard[6] except it's, well, not about sword-wielding mice.) Torchbearer is a great dungeon-crawl-focused system that can really capture grit and difficulty in a way that's a lot of fun, but it's also the kind of game where you can get a total party kill not just by a dragon but also by running out of food and torches, so expect a grimy tough game out of it!

If you're looking for something even further afield, I'd suggest taking a peek at Dungeon World[7], which borrows the core mechanics from indie darling Apocalypse World[8] but applies them to a traditional D&D milieu: that said, I'd actually start with Homebrew World[9], which streamlines and clarifies a lot of the rules, but it might require Dungeon World itself to get a handle on how to run the game. Games inspired by Apocalypse World—sometimes called Powered by the Apocalypse games—definitely play a bit differently—they tend to be a bit more "zoomed-out", e.g. combat being resolved in a fewer high-level rolls rather than playing out a full sequence of six-second slices like D&D—and they aren't to everyone's liking, but I think they're worth trying.

I'm also going to plug my personal favorite tabletop game, Blades in the Dark[10], which is not a traditional fantasy game (although people have adapted the the rules to more traditional fantasy, c.f. Raiders in the Dark[11]) but which I think is super compelling. It's about criminals in a haunted Victorian-ish setting doing odd jobs, and builds a system that's top-of-its-class for doing that, including mechanical support for heist-movie-style flashbacks and a lot of systems designed to let you do risky moves and narrowly avoid failure from them. Some of my absolute favorite TTRPG moments have been in Blades games.

Any of that sound interesting? Want other examples or directions?

[1]: https://paizo.com/pathfinder [2]: https://www.drivethrurpg.com/product/255088/The-Black-Hack-S... with the open content collected at https://the-black-hack.jehaisleprintemps.net/ [3]: https://www.drivethrurpg.com/product/197158/Maze-Rats [4]: https://www.burningwheel.com/torchbearer-2e-core-set/ [5]: https://www.burningwheel.com/burning-wheel/ [6]: https://www.mouseguard.net/book/role-playing-game/ [7]: https://dungeon-world.com/ [8]: http://apocalypse-world.com/ [9]: https://spoutinglore.blogspot.com/2019/05/homebrew-world-v15... [10]: https://bladesinthedark.com/greetings-scoundrel [11]: https://smallcoolgames.itch.io/raiders-in-the-dark

andolanra··on An Open Letter on the Open Gaming License, to Wizards of the Coast
Absolutely and then some! I personally would argue that D&D itself—5E in particular here—is actually a fairly middling tabletop game. It's held back by a lot of historical cruft because even new editions end up being forced to stick to decades-old design decisions for the sake of tradition. A simple example here is the distinction between ability scores and ability modifiers: this is an old D&D-ism and trying to remove it sparks complaints about how it's "not D&D", but it's frankly some unnecessary complexity and other tabletop games lose nothing by dropping scores and just using modifiers.

Apart from the core design, D&D is also pretty middling as a product. Being a DM for D&D is hard—a fair bit harder than running many other tabletop games—and the book are at best a so-so resource: there's a lot of extra prep and careful balance that rests on the DM's shoulders, and doing it right means either falling back part-and-parcel on adventure modules or doing a lot of careful tuning and reading forums and Reddit threads. In an ideal world, the core books would include everything you need to know, but in practice the best DM advice is outside the core books (and sometimes even contradicts the books themselves!) Many other games don't have this problem.

To be clear, I don't think D&D is a bad game, but plenty of other games out there have clearer core designs, better presentations, easier-to-grasp rules, and overall more polish.

andolanra··on An Open Letter on the Open Gaming License, to Wizards of the Coast
There are a truly massive number of RPGs out there with a wide variety of open licenses, especially the various Creative Commons licenses. The indie RPG sphere is massive and includes tabletop systems that range from "mild variations on the core D&D formula" to "almost fundamentally alien approaches to doing structured role-playing". D&D has long-standing brand recognition and cultural cachet, but it hasn't been the only player in this space for decades.
andolanra··on Ten influential programming languages (2020)
Sure is lucky that the title of the post explicitly says "mostly dead" and includes a big disclaimer about how not all of them are dead, then!
andolanra··on C2Rust Transpiler
The short form is that Corrode is effectively deprecated in favor of c2rust. Indeed, Corrode hasn't been updated since 2017, while c2rust still gets active development—last commit as of my writing this was 2 days ago.

It's worth noting that the developer of Corrode was consulted on the early design of c2rust, which means c2rust was able to benefit from hindsight on architectural decisions in Corrode. That ended up leading to a bit of a messy history between the two (c.f. https://jamey.thesharps.us/2018/06/30/c2rust-vs-corrode/ with HN discussion https://news.ycombinator.com/item?id=17436371 —although I believe that after that blog post the c2rust developers did end up acknowledging their inspiration and apologized for not doing so earlier.)

andolanra··on Packed structs in Zig make bit/flag sets trivial
> This all works, people have been doing it for years in C, C++, Java, Rust, and more. In Zig, we can do better.

We can also do better in those other languages, too. For example, in Rust, I can use a crate like `bitfield` which gives me a macro with which I can write

    bitfield! {
        pub struct Color(u32);
        red, set_red: 0;
        green, set_green: 1;
        blue, set_blue: 2;
        alpha, set_alpha: 3;
    }
Don't get me wrong: it's cool that functionality like this is built-in in Zig, since having to rely on third-party functionality for something like this is not always what you want. But Zig is not, as this article implies, uniquely capable of expressing this kind of thing.
andolanra··on The Futures of Inform
It is not at all the point of this talk, but as a side-note:

> …the functional programming language Haskell [compiles] via C, for example…

This is not correct. GHC, the de facto Haskell compiler, targets an intermediate language called C--, which resembles but is not identical to C and was explicitly designed as a compiler target. The point that some languages do compile to other high-level languages is true—Nim and Haxe being some good examples—but it is not true of Haskell.

andolanra··on Ultraconserved words point to deep language ancestry across Eurasia (2013)
This research is built on some pretty shaky ground—including some very loaded picking-and-choosing of vocabulary—and because it doesn't have very strong predictive power, hasn't made much of a dent in the community in the last decade since it's been published. Here's a discussion from the blog Language Log that discusses a lot of the methodological problems that show up: https://languagelog.ldc.upenn.edu/nll/?p=4612
andolanra··on The KDL Document Language
This isn't a unifying standard, which is what the XKCD comic is about. It's not attempting to take other standards and develop the One To Rule Them All—you know, like the first panel of the comic describes—but rather building a different tool that's a better fit for some specific use-cases.

Honestly, if the internet had been around when the screwdriver was invented, the Hacker News commenters of the time would have been linking to XKCD 927 along with snide comments about how hammers and wrenches already exist.

andolanra··on Functional Programming in Go with Generics
I think people have a habit of taking relatively surface-level features of functional programming and focusing on them to the exclusion of the real benefits of functional programming. The use of something like compose3 in the "functional" example here is a perfect example. Sure, function composition is a "functional" thing, but I don't see why you wouldn't instead write something like (handwaving wildly on the specifics, since I know barely any Go and certainly am not up to speed on the generics proposal):

    func getTopUsers(posts []Post) []UserLevelPoints {
            return posts.GroupBy(func (v Post) string { return v.Level })
                        .Values()
                        .Map(getTopUser)
    }
This pipeline style is significantly easier to read (especially without having to put all those extraneous type parameters in your call to compose3!) and doesn't actually lose any of the core advantages of the functional style: purity, testability in isolation, equational reasoning, and so forth. Sure, the Haskell equivalent might use composition… but composition reads more or less naturally in Haskell, and I don't think it does at all here in Go. If your specific approach to "functional programming" makes your code theoretically easier to reason about but practically harder to both read and write, then is it really helping you much?
andolanra··on Do you really want Linux phones
Well then, you should make sure that you're providing a useful service with that data, or allow data collection to be turned off.

Think of it like this: if I'm actively using an app that's giving me turn-by-turn directions, then of course I'm not going to want to spoof GPS, since it would make the app useless to me. But if your app demands GPS data just for ad analytics, then yes, I would absolutely and without any remorse spoof GPS data for that app. In that latter case, I don't care that a developer might be unhappy with it, because as a user I'm unhappy that the developer is slurping up data they don't need!

andolanra··on Python Pattern Matching
It's worth noting that a properly-configured linter will give you an unused variable warning here, since you've bound to NOT_FOUND but not used it for anything.
andolanra··on Pony – High-Performance Safe Actor Programming
I tried using Pony for 2019's Advent of Code, and I ran into this problem. I ended up writing a whole blog post about the way that I tackled error-handling, which I don't think is the solution (especially since the Pony team is looking at new ways of expressing error-handling) but was sufficient for the programs I was writing: https://journal.infinitenegativeutility.com/pony-errors-and-...
andolanra··on A Proposal for Adding Generics to Go
Rust is a reasonably complex language, but—having programmed professionally in both—I don't think it's nearly as complex as C++. Rust features for the most part are orthogonal to each other, whereas C++ features tend to have weird interactions with each other that are really hard to track and understand. (The one place where Rust starts to get messier in terms of feature interactions is async Rust, but luckily you can program Rust just fine while ignoring the async features.)

Consider initialization: C++ has dozens of different ways of initializing objects that in turn interact in complicated ways with move semantics and references and so forth. Rust's initialization story by contrast is straightforward: you just make the thing you want to make using struct or enum literals and maybe wrap those initializers in functions if you want.

andolanra··on Anu: A sound, distributed version control systema
That example, while technically correct, is a little bit misleading. From a practical point of view, the thing that Pijul/Anu both do is not "automatically resolve conflicts" but rather "allow repo operations to happen even in the presence of conflicts". In Git, if you've got a conflict, Git will require you to fix it before doing anything else. In Pijul or Anu, you can continue applying changes—possibly creating more conflicts!—in a way that's guaranteed to never throw away changes. At the end of that, a human still needs to resolve those merges manually.

But there are scenarios in which this avoids tedious human merges. Consider that I'm applying a series of patches which make changes in a file and later on walk those changes back, and run into a merge conflict there. In Git, I could squash those changes to avoid dealing with the conflict, but then I've lost history. I could apply the changes, skipping the relevant patches, but if those patches still contained useful work elsewhere, then I'd have to go in and resolve those problems manually.

In contrast, this same scenario in Pijul and Anu would just trivially work in a way that didn't produce conflicts. I would apply the sequence of patches, and one patch would produce a conflict… but because they can keep doing work in the presence of conflicts, then they could keep applying subsequent patches and apply the patches which walk back the changes, and in that resolve the conflict automatically, but unlike the Git approach where I flattened the changes first, I would still have the full commit history associated with that sequence.

Now, that doesn't mean that Pijul or Anu will automatically fix all merges. If you have two separate code edits to reconcile, you might still need a human in the loop to reconcile them. But the fact that they can keep making changes in the presence of conflicts allows them to avoid a certain kind of "busywork" that comes with managing git history.

andolanra··on Ask HN: What RSS Reader do you use?
I've used a self-hosted version of Stringer for a while: it's a barebones single-user RSS reader with very few other features. Probably not great if you plan on following lots of feeds in different categories, but it's perfect for me: https://github.com/swanson/stringer
andolanra··on Gitter is joining Matrix
Matrix is based on an open documented protocol, which means it welcomes multiple implementations. There are several terminal-based clients, which you can see on their current client list, and I wouldn't be surprised if more appear over time: https://matrix.org/clients/

There are also Matrix-IRC bridges that already exist: https://matrix.org/bridges/#irc

andolanra··on Handles Are the Better Pointers (2018)
You should probably read the whole article, because it describes strategies for dealing with exactly this problem in the section under Memory safety considerations and then again in the update at the end. The short form is: if you have a bounded size on your array, then you will often have unused bits in your index pointer; you can use these to store a "generation counter", which needs to match the slot. You can now re-use indices, because you can compare the generation of the index (stored in the extra bits) with the generation count in the slot, and if they match, it means that the item has been 'freed' and a new thing is in the slot.
andolanra··on Division by zero in type theory: a FAQ
Pony now includes both partial division (which raises an error when dividing by zero) and checked division (which returns a tuple of both a result and a boolean which indicates whether an error occurred): https://tutorial.ponylang.io/expressions/arithmetic.html#par...

For what it's worth, I'm willing to argue that Pony is making a strongly motivated choice by making x/0 return 0 by default with respect to their specific error-handling model. In general, I don't think it's as much of a problem as you might imagine. The average function is a perfect example: if I naïvely implement it in such a language, then it would mean… that average([]) returns 0. That doesn't seem terribly bad to me.

andolanra··on PEP 622 – Structural Pattern Matching
It's worth noting that there's a truly massive amount of precedent in other languages for Python implementing it using the syntax as proposed. Languages that have or are planning to include pattern-matching where the pattern syntax exactly mirrors the expression syntax like this include Rust, Swift, OCaml, Haskell, C++, Ruby, Erlang, and many, many more.

I understand the worry that newcomers might struggle, but I don't think it's going to be the case: newcomers regularly learn the languages listed above without stumbling across that problem. And if Python did choose a syntax like the one you're proposing, it'd also be the odd one out among dozens of mainstream languages including this feature, which I think would be even more confusing!

andolanra··on Basecamp’s founders are trying to start an email rebellion
Nobody I know who used Inbox feels that the current Gmail interface—even with some Inbox features backported—is an acceptable substitute for Inbox.
andolanra··on I Replaced My MacBook Pro with a Raspberry Pi 4 8GB for a Day
This is literally the last paragraph of the post:

> But, sadly, I don't think this year is the 'Year of the Linux desktop'. In general, I think 'Linux on the Desktop' for a mainstream audience is always going to be 20 years away, just like nuclear fusion.

andolanra··on I Replaced My MacBook Pro with a Raspberry Pi 4 8GB for a Day
I'm not sure why the conclusion is, "Linux on the Desktop isn't possible," when the big blocker the author had was pretty consistently finding and installing software compatible with a small ARM64 machine. (That's not to say it's not a valid conclusion to draw in general: just that it's a bit of a non-sequitur for this article.)
Page 1 of 10Next →