Hacker News new | ask | show | jobs
Show HN: The Flint Programming Language (github.com)
4 points by zweiler1 19 days ago
I am happy to finally announce the language I have been working on for the last 2 years, [Flint](https://flint-lang.github.io)!

Flint is a high-level, statically and strongly typed, compiled language which centers around transparency as its core pillar. The compiler is entirely written in C++. It originated from one simple idea and core concept:

> What happens when you center the whole language on an ECS-inspired composition-based paradigm?

And so the journey began. The core idea is simple: data and functionality are separated and then composed deterministically into larger entities. This idea is not new at all, ECS exists since a long time. But a composition-based workflow can only be "emulated" in Object-Oriented languages and I find it often painful or unergonomic.

In Flint, composition is the core paradigm. I have put great effort into making it ergonomic and "just work". The result is a system which can be described as a cool mix of OOP and ECS. I gave the "new" paradigm a name, since nothing quite like it exists yet, even though the ideas it is based on are well known, the *Declarative Composable Modules Paradigm (DCMP)*.

The combination of a high level + transparency as a core pillar is a bit unusual. I have put great effort into finding a good balance. I found out that these two things are not mutually exclusive, there is a middle way in which a design can be both high level and transparent. Flint might be best described as "middle-level" as a result: You write high level code but you can see the low level runtime and execution beneath too if you want, as this focus on transparency directly results in shallow abstractions.

Most developers are more used to OOP workflows rather than compositional workflows, it's just more mainstream. So, if you cannot live without it, Flint might not be for you and that's okay. Also, I am also sure that Flint won't be for everyone because of it's split focus on being high level and transparent. It will feel too high level for some or too low level for others. But if the core idea and mentality excites you, please give it a fair chance.

The time has come where I am confident enough in Flint to search for people to try it out and give feedback on it. Many features are still missing but the general vibe and direction of the language can already be seen. The `0.4.0` version is the 20th release so far, the first initial version was released a year ago. I am now moving into the `0.5.0` release cycle which will bring generics, type constraints, compile time code execution, the standard library and more. You can look at the entire roadmap [here](https://github.com/orgs/flint-lang/projects/6/views/1)

The [Wiki](https://flint-lang.github.io/wiki) is in a very good state, it is kept updated with every release made. Every example in the Wiki works and I did My at explaining it all. The language's core value is transparency, so there is nothing to hide about it.

I intended to add a small example here, but the character limit tripped Me over a bit here. There are plenty of examples elsewhere, so if you are interested you will surely find enough examples :)

The project is in late beta. All implemented features work reliably, as all wiki examples compile and run as intended. There are still missing error messages and unexpected edge cases (as expected from a single developer).

If you're interested, try it out, give feedback, open issues, and feel free to join the Discord. Let's discuss Flint!

(Also, I may not be aware of some industry-standard names for some systems. If you encounter anything I gave a weird name where you think "wait something like that already exists" please let me know. I try to use industry-standard terminology as much as I am able to. I hate it when new names are made up for something which already exists.)

2 comments

Is it memory safe? Can I cause use-after free, double-free, out-of-bounds access errors or something similar? What about race conditions?

In your DCMP examples you demonstrate how types can be composed from smaller components. How does it work under the hood? Is it possible to create an abstract container for Legs and store inside it any object having this component?

It is memory safe, use-after free, double-free, out-of-bounds access errors etc are all prevented by default. Of course there might be edge cases (bugs in the compiler) where a double free bug happens, but this should not be possible at the language level and then is a compiler bug (I fixed a lot of such bugs over the last few months). You can control them via specific compile flags to make it unsafe, but the default is all properly checked.

Race conditions should be prevented by design, but I haven't actually implemented multithreading at all yet, so I would rather not make any statements about it since it's not done.

I also posted the language on reddit and got quite a lot of feedback from it, and now I am redesigning many parts of it since it was a bit messy (static guarantees, that it basically has nothing to do with ECS any more etc).

I don't know what you exactly mean with an "abstract container". But yes the data `Legs` can be used across many different entity types, you can use and store that component in every entity type you would want to.

Under the hood it essentially is just owned composition, so the entity is just a tuple of pointers to its owned data components, nothing more.

> It is memory safe

How? Do you have references/pointers? Are all objects except basic scalars heap-allocated and GC-tracked? Do you perform some clever compile-time analysis?

> I don't know what you exactly mean with an "abstract container"

I mean something like std::vector<Base> (from C++) able to store pointers to instances of derived classes.

> entity is just a tuple of pointers

It looks like having just a pointer to a Legs* component I can't access the whole object (having other components). Am I right?

All heap-allocated values like arrays are tracked at compile-time using a scoping system, when they go out of scope they are freed, similarly when variables are overwritten, assigned to etc "garbage" is detected by the system and at these points the freeing code is inserted. Every value can only be pointed to by one single value, so there are no "memory sharing semantics" (two variables pointing to the same memory) expect for memory managed by the automatic memory management system, DIMA, which is just a fancy per-type ARC-based incremental arena allocator, but these types (`data` and `entity`) are the only types managed by it which then means that these values can be pointed to by multiple variables since the RC is known, so it's safe.

Ah I see, at the moment there are no container types at all since generic types and thus monomorphization is not implemented yet. There is no inheritance in Flint, but you will be able to define type constraints on generic types to get something similar. This is part of the `0.5.0` release cycle but not implemented yet.

Yes, just from a ponter to Legs, you cannot access the entire entity, that's true. However, through polymorphism you are able to access an entity through a `func` modules view. (The `func` module is in the process of being split into two types, a polymorphic `interface` and a non-polymorphic type, I have no good name for it yet. I realized that it's very messy at the moment and definitely needs to be improved).

i really like it. Keep going!
Thank you, I definitely will.