// What if a function and struct are the same 'thing', I'll just call it a 'block'. MyStruct { x : int = @ // @ here means not optional, *must* provide this when constructed. y : int = 0 // Default but optional initilization } // And a function has the same syntax, just with code // There is no function argument signature, it uses the decelerations in the order they appear. my_func { a : int = @ b : int = 2 // Default for optional argument return a*b // Type deduction, if needed could force a type with a cast. } m := MyStruct(2) n := MyStruct(4, 3) x := MyStruct() // ERROR: missing arg x := MyStruct(@) // Maybe allow an override, here memory is uninitialized. // Is equivalent to // struct my_struct_t { // int x; // int y; // }; // my_struct_t MyStruct(int x, int y=0) { return my_struct_t{x, y} } // And similarly the function... g := my_func(1) f := my_func(1,2) // What if member functions where just regular functions that allowed both an // OOP and non-OOP syntax // Example: Implementing a class MyClass { name : string = @ } getName { self : MyClass = @ return self.name } my_obj := MyClass("Steve") // These 2 calls are equivalent: getName(my_obj) my_obj.getName() // Maybe whitespace, its generally eaiser to edit since you don't have to close brackets // everywhere and looks cleaner. But I'll stick to braces for the examples. // The use of : like Python could also be confusing with the decelerations. my_func: x : int = @ return x*x // The 1st struct is effectively an implicit factory function, so in some ways everything is a function: my_struct { // allocate_memory must either be on the stack or heap depending on the caller // The ! here is like := but the arg isn't overridden by the caller // (just theoretical since this entire section would be done by the compiler, but it could be used elsewhere?) self != &allocate_memory(sizeof(my_struct)).cast(my_struct) self.x := default_of(my_struct.x) // These would be overwritten by the caller if they provide args self.y := default_of(my_struct.y) return self } // There probably needs to be some simple way to tell the difference // between a function argument that should be given when it's called // and an internal value to accidental prevent misuse and provide // documentation. Maybe a keyword or sub block. my_func { a : int = @ // This *must* be given when called. b : int = 1 // Default arguments! But still overridable by the caller. a = another_func(a)+12 c := another_func(b+1) // Theoretically this could be overridden by the caller providing a 3rd arg d = my_struct(a, c) return d } // In this example everything before the --- is a function argument and // anything after isn't. my_func { a : int = @ b : int = 6 --- ans : int = a+b+3 return ans } // Eg... my_func(2) // a is 2, b is 6 my_func(2, 9) // a is 2, b is 9 my_func(2, 9, 10) // Should error as 'ans' is internal // Could be an alternative to --- to separate a arguments in a function form the internal values. some_thing { x : int = @ // No default so this is required y : int = @ 99 // This is optional z : int = 99 // This isn't considered part of the function signature. Just a normal value initialization. a := 99 // Type deduction } // Or using a subblock to specify arguments some_thing { args { x : int = 100 y : int = 100 } internal: int = 100 } // With whitespace it starts to look a little like YAML some_thing: args: x: int = 100 y: int = 100 internal: int = 100 // nested subblocks allow for modules/namespaces math { point { x : int = 0 y : int = 0 } add { a : int = @ b : int = @ return a + b } add { // Function type overloading? a : point = @ b : point = @ c := point( add(a.x, b.x), add(a.y, b.y), ) return c } } // Allow nested... big_func { little_func { a : int = @ return a * 1000 } b := little_func(1) c := little_func(2) return b+c } // Default getters and setters? Or allow overriding values with a function if desired? some_thing { x : int } // Standard usage my_thing := some_thing(3) my_thing.x = 4 // is equivalent to my_string.x(4) y := my_thing.x // is equivalent to y := my_string.x() // Later if the programmer decides to use a setter/getter some_thing { x_ : int } x { self : some_thing = @ self.x := self.x // By default self.x will be assigned itself (which should hopefully be optimized out), unless its overridden as the 2nd argument to the call --- return self.x // With optimization, I assume we can ignore this if it isn't used? } // Function tricks? p := math.point(3, 4) // Normal assignment, no pointer p_ptr := &math.point(3, 4) // This would be the address of the returned value, so maybe use | make a functor? add_stuff := |math.add(@, @) // Maybe using @ here overrides the initialization requirement // leaving dangerous uninitialized memory (maybe add 'unsafe' keyword/attribute). // Otherwise just pass in a default. // Also consider the following possibilities add_stuff := math.add // What happens here? An alias? // Pointer to a function add_stuff := &math.add // Functors, the memory would probably be allocated in add_stuff so it would't need a stack. // or any other memory allocation. (although compilers do tricks like that already). // But in this case at the cost (or feature) of possible side effects of previous functions run... add_stuff := |math.add(@, @) one_plus_two := add_stuff(1, 2) three_plus_four := add_stuff(3, 4) add_stuff.b = 10 // Settings args this way should work too, as we didn't set // add_stuff.a so the previous usage of this functor is kept. three_plus_ten := add_stuff() // Keywords for inline? How much should optimization be left to the programmer?