I'm blown away by the completeness of this. The IDE is extremely full featured, albeit a little weird in places (eg scrolling with my laptop touchpad basically doesn't work). Love the Windows installer. But wow, go to definition, refactorings, debugger with great UX, callstack, etc.
Also love the example game. It's written so cleanly that I learned a thing or two about 2d gamedev just from scrolling through the code.
I love the little pragmatisms in the language, eg:
for (let entity in gGameApp.mEntities)
{
if (let enemy = entity as Enemy)
{
// .. do something with each enemy
}
}
That if/let/as combo there is something I'd have loved to have in many languages.
Also hats off for switch/case without break. Finally! I also love how you're mixing C/C++ "global enum value names" convenience with namespaced safety:
public static Result<(char32, int32)> TryDecode(char8* buf, int bufSize)
{
...
return .Err;
// not Result.Err, because the return type is given in the signature
}
I know that these little syntactical things are not the key challenges of language design, but obviously they're the first things I see, and I like the amount of attention you've given them. You really only have the opportunity to get them right at the very beginning.
I think the docs are a little sparse still (but that can be expected, of course). Eg:
I love that you made it easy to destruct fields like that, right there in the initializer expression, btw. Will probably kill the need for 90% of destructor methods out there.
- Apparently this converts floats to ints:
(.)(mX - entity.mX)
Or at least it does in HeroBullet.bf:15. I'm thinking maybe this does something similar as `.Err`, in that the dot makes it cast to whatever the expected type is in the function being called? I'm not sure though, it looks like a super powerful feature, maybe :-)
Finally, I believe .bf is also used for that other super convenient and performant language, Brainfuck. I doubt that causes any practical problems, but then again, why not just .beef?
Thanks for the checking out the language thoroughly, that's appreciated.
With "delete _", you are correct, the "_" refers to "the value in question", which is mRand in that case. When you have a switch statement, the "_" refers to the value being switched over.
The "." type is a special type meaning "the expected type here". So that's explicitly casting to the expected type, since an implicit cast from float to int is not allowed.
And yes, my apologies to Urban Müller for overloading his file extension, but it seemed the chances of that actually being a problem for someone were acceptably low and I really really preferred ".bf".
Come to think of it, this is an excellent opportunity for even broader interop! Just make the compiler parse any series of, say, 5+ consecutive !?,.+-[] characters not as a parse error but a Brainfuck expression! What could possibly go wrong?
Wrt the "_" and the ".", if those are documented at all, maybe make the other sections in the docs (eg the one on destructors) link back to there. I didn't read the docs from top to bottom, instead preferring to refer to them when I saw code I didn't understand. I bet I'm not the only one that likes to learn like that. Note that everything I write here is intended as a friendly suggestion and feel free to ignore all of it. I'm just a random guy from the internet.
Finally, wrt casting: I've always found that C-style "(type)" casts are weird and messy and don't fit the rest of the feel of the code. I wonder why you took them over? You also have "as" for dynamic-casting, and personally I think those are a lot more readable. Did you consider stealing some ideas from Kotlin and TypeScript wrt this to unify the two? Let me know if I'm stepping over a creative boundary here, but I'm personally quite mesmerized by Kotlin's !! operator[0], which simply forces a value to be not-null and throws otherwise. That's essentially what "(int)foo" does in Beef when foo can't be casted to an int, correct?
Then, if Beef had such an operator, then prefix casts would be semantically equivalent to "(foo as int)!!", right? Or maybe even the super-nice-on-the-eyes "foo as int!!" if precedence rules allow. Obviously a naive compilation of that would be slower (a dynamic cast followed by an if-null), but it seems like a pretty simple optimization to add.
Maybe "foo as .!!" could even be shortened to "foo!".
Given that you have int? and ?? and ?. already, I could imagine you considered something like this already and decided against it? Sorry for the rambling :)
Again, great job! Really looking forward to playing around with it more. Still totally blown away by the completeness of all this. I can't understand how a single person can pull all of this off on their own time.
I was a bit puzzled by the SDL2 wrapper by the way (SDL2.bf) - I can't find any place where it refers to a .lib or even a .dll file. How does this work? I tried to find a Visual Studio-esque "project settings" screen with deeply hidden list of .lib files but to no avail. How does Beef know where SDL is?
Also love the example game. It's written so cleanly that I learned a thing or two about 2d gamedev just from scrolling through the code.
I love the little pragmatisms in the language, eg:
That if/let/as combo there is something I'd have loved to have in many languages.Also hats off for switch/case without break. Finally! I also love how you're mixing C/C++ "global enum value names" convenience with namespaced safety:
I know that these little syntactical things are not the key challenges of language design, but obviously they're the first things I see, and I like the amount of attention you've given them. You really only have the opportunity to get them right at the very beginning.I think the docs are a little sparse still (but that can be expected, of course). Eg:
- I found it hard to parse this:
There's docs about it here https://www.beeflang.org/docs/language-guide/datatypes/initi..., but the "delete _" part isn't really explained. What's that underscore? I can infer that it probably means "this value", but I can't find it back.I love that you made it easy to destruct fields like that, right there in the initializer expression, btw. Will probably kill the need for 90% of destructor methods out there.
- Apparently this converts floats to ints:
Or at least it does in HeroBullet.bf:15. I'm thinking maybe this does something similar as `.Err`, in that the dot makes it cast to whatever the expected type is in the function being called? I'm not sure though, it looks like a super powerful feature, maybe :-)Finally, I believe .bf is also used for that other super convenient and performant language, Brainfuck. I doubt that causes any practical problems, but then again, why not just .beef?