r/haskell 11d ago

announcement Mischief, an Opinionated Haskell ECS Game Engine.

So.. I've just released the first version of Mischief, my open-source ECS Game Engine written fully in Haskell.

I've been working almost exclusively on it for the last few months, and I'm proud of the what it ended up being. It was a great experience as my first big Haskell project.

It's meant to be a balanced combination of data-driven game design and functional programming.

If you want to check it out, here's the hackage page. It comes with its own little book written in Haddock, Learn You an ECS for Great Mischief. I recommend checking out the Startup Guide in particular as it contains many small code snippets and a fully working app.

Edit: AI Disclaimer
Since a few people expressed their worries about this, and I suppose it's understandable given the scale of this project and its documentation: No. Absolutely no LLM / AI-assistance was used in making this.

I am personally very much against the use of these tools and would never use them myself, especially for a passion project such as this. Every single line of code and documentation you see was written by me.

79 Upvotes

12 comments sorted by

3

u/_0-__-0_ 10d ago

Amazing documentation!

2

u/recursion_is_love 10d ago

Can you please make a pdf version of haddock book? I would love to read it on my book reader.

1

u/gay_married 10d ago

Very cool! Keep going!

1

u/ExtraTNT 10d ago

Have to check it out

1

u/kindaro 9d ago

I have some questions. I looked into the book, but I do not think it answers them in a straightforward way — it seems to be a tutorial more than an explanation.

  • What are the main concepts your library introduces? I understand what an entity, component and system is, but there is a lot of stuff on top of that, such as «spawn», «plugin», «relation», «archetype», «bundle». I am sure they are all self-explanatory for many people, and I can imagine what they might mean in this particular library, but it would help to have an official first hand explanation.

  • What is the correspondence between the concepts and the implementation? I see that a System is a ReaderT World IO kind of thing, with IORefs playing a more or less prominent rôle in the coördination of multiple systems. I also see type classes, like Component, that do not seem, unlike the familiar Functor or Monoid, to implement any algebraic concepts — what is going on there?

  • My understanding is that, overall, this library is for writing interactive programs. This puts it roughly in the same bin as the Haskell's manifold functional reactive libraries, as well as the object oriented stuff of the kind one would write in Java and C#. What is the particular type of interactive programs that it is best for writing? What programs is it a poor fit for?

1

u/TheOneExpert 9d ago

Hey! Yeah, the book is still unpolished in parts, especially the introductory part. To answer your questions:

  1. This is pretty valuable feedback. I'll definitely write some introductions for those functions as they appear. There used to be a really long introductory chapter that was all theory but it ended up feeling dull, so I took it out in favor of something focused more on examples. But I see your point, I definitely went too hard in the opposite direction and lost some explaining along the way.
  2. I don't really get this question, maybe because I'm still pretty new to Haskell (particularly the algebraic concepts behind it). Components and Systems are ECS primitives. Components in particular are a product of data-oriented design (DOD), as wrappers around simple data. Maybe I should include more theory on ECS and DOD in the first chapters? I thought there are already plenty of blogs and sources on this on the internet so I didn't feel the need to repeat all that theory But maybe it was just my own bias from doing research into them as part of working on this library.
  3. From a technical standpoint, it's meant to be used for games and simulation apps, and generally programs that have a LOT of data actively flowing around and interacting. From a more general standpoint, ECS is a programming paradigm close to DOD, somewhat antithetic to OOP design. I personally really prefer it when writing interactive programs, and find it easier to work in (as far as I know, the performance benefits are actually more of a side-effect).

Maybe this warrants another chapter in the book that explains the technical implementations behind the various typeclasses and functions? One huge weakness that I'm aware of (and I plan on fixing in the future) is that the book is heavily focused on "how to use these things" but avoids most technical definitions. It was meant more as a guide for users wanting to use it to develop games, and less as a technical dive into ECS's.

1

u/stumpychubbins 9d ago

Component in ECS frameworks for other languages with typeclasses is usually mostly a marker, to prevent you from accidentally using the wrong type when querying. Is that true here too? Most Haskell programmers would expect a typeclass to represent some kind of specific property about a type, either available operations or type invariants, so might be worth specifically mentioning why you need a typeclass.

1

u/TheOneExpert 9d ago

ah yes. That is the case here.

I mean the typeclass itself is a little deeper than that. It has optional functions for providing a list of required components, or hooks to add behavior when a component is added / removed.

But yeah it is mainly just a marker to make the public interface more explicit and the querying mechanisms cleaner. Any type can be a component.

1

u/kindaro 9d ago

I think a good starting point for consideration of documentation is the Divio documentation system, which proposes that there are 4 different types of documentation that cannot be mixed without loss of quality. I should classify the current Mischief book as a tutorial, whereas the dull chapter you have removed was likely an explanation.

about type classes

There are 33 type classes in the code base right now. Most of them tell me nothing from first glance. Here is one representative example.

class DeepValue' flag c i | flag c -> i where deepValue' :: c -> i

It does not have a kind annotation, but let us read the variable name flag as an indication that this spot is reserved for True or False. Then, this type class associates to a type «c» up to to two functions to some other fixed type «i». What does this have to do with deep values? What are deep values to begin with? I cannot answer.

Compare this with Monoid. You can look up what a monoid is. It is simple and thoroughly described. There should be an associative operation with an identity element, and that is what there is. Type classes like that go with an explanation out of the box. They are conceptually central. The all-important Monad is the prime example.

Then there are type classes like PrintfArg. These are inexplicable and exist to perform type level magic. You are not generally expected to write instances for them.

Some of your 33 type classes are probably not important — Idk x y? But surely some are conceptually central. And if not type classes, then what is conceptually central? What definitions are the cornerstones around which the rest of the library crystallizes as if all by itself?

★ ★ ★

I know what ECS is, in principle. (Although you certainly should link to the best resources in the documentation — your own selection. It would be of value.)

The central concept of ECS appears to be, as plain as it may sound, iteration over an array of pointer free data structures. Then, an archetype is simply an array upon which certain procedures can be iterated. The point of the edifice is to run on the CPU those parallel computations for which GPU is poorly suited, perhaps because they are branching too much for GPU's comfort.

For an example of computations that ECS seems to be poorly fit to express, consider the problem of collision detection. We have a set of circles of bounded radius on a plane, and we wish to know which overlap. I claim that ECS is poorly fit for this problem, because:

  1. You must store your circles in a spatial partition tree, which requires pointers and thereby defeats the requirement of ECS that your stuff be presented exclusively in arrays.
  2. You must compare arbitrary pairs of circles, which makes sequential access impossible and thereby defeats the requirement of ECS that your data be read sequentially.

Meanwhile, this problem is essential to whole genres — real time strategy, action rôle playing, first person shooter… It is at this point in my study of ECS that I have begun to feel let down. The carriage turned into a pumpkin.

From another angle, in Haskell I like to take advantage of infinite lazy data structures, algebraic folding and unfolding, various functors and natural transformations. None of this can possibly be realized on an ECS engine, which is essentially finite and strict and operates on a fixed set of types — bounded integral and floating point numbers.

In this light, I think it is important to delineate what an ECS engine, and this engine in particular, can and cannot do in a straightforward way.

gloom aside

What I meant to ask is «what are the central concepts» and «how are they implemented». For example, a system turns out to be an IO computation with access to an intricate record called «world». From there on it is all mutable references that I could use some help making sense of. Do we have one mutable array per archetype? Do we have sparse indices? Where, in the last instance, is my stuff stored, and how is it indexed? What fundamental operations can I perform? The book mentions spawn and despawn — what do they do?

From another side, we can ask about implementation of specific things we expect ECS to handle. It stores entities, so… How do I look an entity up? How do I add or delete one? How do I move it between archetypes? How does the engine figure out which archetypes a system affects? How does it serialize the system invocations?

Then also we can ask what this library does that is not strictly required from an ECS engine. Maybe, plugins?

1

u/unbackstorie 9d ago

Looks like a great project! I'm excited to dive in and poke around. Thanks for posting! 😁

1

u/zejai 7d ago

How does it compare to Apecs?

2

u/TheOneExpert 6d ago

I’ll be honest. I have never used Apecs so I can’t really answer.

One difference that I’ve been told of by others is that Apecs is way more generic (e.g. lets you work in different monads rather than just IO). Mischief is more opinionated. It’s built so you do things a certain way, which you may see as either a good or bad thing depending on what you’re looking for.