nickmqb / muon

Modern low-level programming language
MIT License
770 stars 26 forks source link

for loops and null #5

Closed wilberton closed 5 years ago

wilberton commented 5 years ago

First of all - really like the look of this language! Secondly, is there a discord or slack channel for discussion?

Lastly, I noticed in the compiler code a fair few places with this kind of pattern: if list != null { for i in list { ... } }

Is there a good reason for not allowing for i in list_that_is_null {} to be valid (and a nop)? It seems it would result in less code and remove a possible source of bugs.

nickmqb commented 5 years ago

Thanks, glad you like it :).

There's no discord/slack channel yet, but I think that's a good idea! I'll have a look at setting something up in the near future. I've filed #6 to track.

I see two arguments against adding an implicit null check around for loops:

  1. The semantics of null depend on the situation. For example, as you saw, the compiler uses null to mean "empty list". However, another program in another situation may use null to mean "uninitialized" (or some other meaning). In the latter case, it would not be OK to skip the for-loop, as we'd be silently ignoring a conceptual error condition (trying to loop over an "uninitialized" container). This goes against the design principle of failing fast.
  2. Performance considerations: would require an extra cmp and jump instruction for each for-in-loop.
wilberton commented 5 years ago

Well I guess the semantics would be that an uninitialised list would be considered empty. But I get that that could cause a bunch of complexities if you wanted to for example get the length of an uninitialised list, or call some other operation on it. I'm used to using stretchy_bufs in C, and they tend to work this way, buf_len(NULL) == 0 - so things just work. Obviously there are many more complexities with the list type in muon, so it's probably not possible to do it nicely.

Looking forward to the discord.

nickmqb commented 5 years ago

Yeah, I agree that it's nice to have "empty" as the default value of a list: fortunately, Muon kind of has this.

Muon defines List<T> as a type with reference notation, meaning that you get a pointer by default, hopefully that is not too confusing for users (I'm still debating whether to keep this feature). Also see: https://github.com/nickmqb/muon/blob/master/docs/muon_by_example.md#reference-type-notation

So you could use $List<T>, which refers to the list struct itself; it can never be null and it doesn't require explicit initialization. You can get a $List<T> by using the struct initializer for List directly, or by specifying the $ as part of the type name, e.g.:

my_list := List<int>{} // Allocate list struct (dataPtr, count, capacity) on stack and zero initialize it
my_list.add(123) // This works

or:

SomeStruct struct {
    items $List<int>
}

The compiler doesn't use the $ approach mostly for practical reasons: the C# interpreter which is used for bootstrapping doesn't support the $ syntax.

It's not quite the same a stretchy buffer though, which stores its count and capacity next to its data. You inspired me to take a stab at writing a StretchyBuffer implementation in Muon :), just to see what it would look like. See: https://gist.github.com/nickmqb/1df778babcc42e87ffdfa458d867afef

Disclaimer: untested code, it doesn't offer all of the sb operations, and it's a bit more awkward to use than the c version because Muon doesn't support [ ] for custom types.

wilberton commented 5 years ago

Ah ok, that's starting to make more sense now. I think that's all quite clean - #RefType is basically like a C# class then?

nickmqb commented 5 years ago

Yup, it's comparable to a C# class in that sense. And by using $ you can use the type as a struct (which you cannot do in C#).