Do you think this syntax for ownership is simple for beginners to understand?

I'm working on a new programming language (Flogram) and largely behind the scenes it automatically allocates memory using Rust ownership and automatically threads the code for the programmer.

The ownership rules I'm using are:

  1. When calling a function you can pass without any modifier and it passes by value (or perhaps under the covers by immutable ref depending upon what the compiler guesses would be faster). Or you can use the 'mut' modifier to pass by mutable ref allowing the object to be modified.

  2. If you create objects or structs using 'new', you can pass them into functions and modify them by ref but you cannot return them from functions. If you use 'gc' then they will be created using the garbage collector and are allowed to be returned from functions.

  3. You can create arrays or hashmaps or memory pools or other data structures and pass those into functions as objects and populate them and in this manner also pass new objects up the stack.

  4. If one object is pointing to another by reference, the one being referred to must have been created at the same time or before the other one (or belong to the same collection data structure.. array, etc). Or it must be garbage collected. I don't like this rule but seems nessecary to guarantee memory safety.

That right there seems like it should cover everything related to Rust ownership but be a little bit easier to digest and beginners can always use 'gc' to create garbage collected objects and not think about it, then optimize the parts that need it later.

Alternatively maybe get rid of gc rules. And for consistency, don't allow returning anything from function, everything returned is returned by mut.

But I came up with it, understand it well, so maybe it seems simpler to me than it would a beginner programmer. Thoughts? Can you think of any ownership edge cases that this doesn't cover? Would you consider using this language?

I dn't think these rules are that easy. The rust ownership rules are far easier than these rules even for a newbie like me.

It feels a bit confusing to me. Though I am probably biased by my familiarity with Rust ownership already.

Maybe some clarifying questions are in order.

  • How is your gc keyword fundamentally different from Rc<T>? Reference counted items get "garbage collected" when the count drops to zero. They also allow shared ownership. It's pretty magical.
  • What is the rationale behind point 4? It appears to imply that it's possible to create references to things that don't yet exist. An assumption I could make is that it does exist, but ownership hasn't yet been transferred to its final location (e.g. a memory pool). FWIW Shared ownership does resolve this issue at the cost of reference counting overhead.

Thanks guys. Not what I was hoping for but I appreciate the honest feedback.

  1. gc is different because it uses garbage collector over Rc, which means it can't end up with reference loops & is often faster in concurrent environments

  2. Other way around, it has to have been created and outlive the reference to it.

I could ditch gc which is extra complexity and function returns too.

So values have to be created in the calling function, passed in by mut, then they can be changed by the function and returned.

I'm trying to prevent people having to think at every single function call about how they are handling ownership and have it mostly just work.

Then the two rules become:

  1. Create objects first and pass in mut, but don't have to think about return as a separate concept.
  2. If you link to an object by ref, it has to live at least as long as the object linking to it.

If you want something like a garbage collector so you can reuse memory, you can create a memory manager before and objects you are pulling from it and allocate from it instead and now the memory manager has to outlive ref, not the object creation itself. Rarely have to think about that one but like RC, nice every once in a while. Or use the global one.

Aren't you arriving at a heapless language (only stack allocation) this way? Clean, easy and safe, but somewhat limited.

That's why I'm suggesting having a specific garbage collector or otherwise a way to access heap should you specifically need it to make it easier to get around the rules.

Arrays, and other dynamically sized collections will use heap under the covers (unless coder tells us it's fixed size), the reference to it is the only thing stored on the stack. They can be created then passed down the stack.

Do you think this syntax for ownership...

People so far seem to be focusing on the ownership model, not the syntax, which is good because:

  1. Syntax is the most superficial thing about any language. It's the easiest thing to understand and adapt to. Parsing is more or less solved. Just pick a syntax and use that as the platform for ironing out the wrinkles in your ownership model. Or just borrow another language's syntax to start with, and once you have something you like, consider changing the syntax to make it less noisy.

  2. Syntax is the most superficial thing about any language. Because it's the easiest thing to understand, just about everybody has an opinion on it, and because it's easy to adapt to, all those opinions are wrong except mine. No matter what syntax you pick, people will complain that it's ugly, makes their eyes bleed, hides bugs, confuses newcomers, etc. No matter what syntax you pick, those voices will get louder the more popular your language is. Asking the Internet for advice on syntax is like asking for dating advice: you're going to get a bunch of contradictory suggestions from people who have totally different priorities, many of whom will call you stupid for doing anything else, even if you are the most popular language in the world.

I'm not saying syntax is unimportant, I'm just saying it's less important than the underlying semantics you're hoping to wrap in it. It's a good thing that Rust syntax makes borrowing more "weighty" than moving, for example. But that's secondary to the entire ownership model that makes borrowing also the mentally "weightier" choice.

Regarding non-syntax: it seems like you haven't really figured out what the ownership model should be yet, which is fine. The fact is though that ownership itself is a difficult thing to wrap one's head around. If a goal of this language is to be approachable to beginners, its ownership model is going to be the vehicle you use to explain the concept of ownership to those beginners. So maybe that means you should make it as simple as possible: just 2 or 3 kinds of ownership, and leave the complicated stuff (like shared mutation) out entirely. Or at least make it incremental: if you have 10 ownership kinds, make them independent of each other so that a novice can write lots of code without having to understand more than 1 or 2.

Mostly agreed @trentj Syntax was probably the wrong word :slight_smile:

Though I do believe people tend to go for more of a C like syntax since most programming have worked with such a syntax. That aside. Fully agree that my real goal is to figure out how to simplify concepts as much as possible. Or rather, simplify things as much as possible while maintaining the ability to write very fast programs. It's less about attracting beginners more about getting peak performance while maintaining thread and memory safety.. as simply as possible. Which even for those who are not beginners, should improve productivity.

I thought I had it mostly figured out until as I was writing sample programs to think through how it actually would work.. I found a case that didn't work. So I'm trying to figure out the easiest to understand solution to it but yeah, it's affecting other parts to as I try to merge it in with everything else and as you said, simplify. But for the most part yes, on this front I think you are right, figuring out how to reduce concepts rather than syntax.

Thank you for the input!