Values/structs?

Community Forums/Monkey2 Talk/Values/structs?

marksibly(Posted 2015) [#1]
One thing I often miss from c++ when using monkey is its handling of values.

Values differ from objects, because their 'fields' are always copied when they are passed to functions or assigned to vars. This means when you modify a value, you know you're not messing up anyone else's copy of it.

It would be a big addition, but it'd open the doors to some pretty cool stuff:

* GC speed ups - Values don't need to be GC'd. Local values in functions will be fully 'on the stack'. Great for iterators etc.

* Operator overloading - I don't really consider this viable if operators have to 'new' or allocate from a pool or something to create temporary values.

Here's one way it could work:

Class Vec3 Value
   ...decls...
End

function main:int()
   local p:=New Vec3() 
   local q:=p 'copies all values from p
end


The 'new' is still there mainly because of conflicts with casting. Actually, keeping it helps out generics:

class Factory<C>
   method Build:C()
      return new C
   end
end


...this would be able to return an object or a value.

We could start getting very c++ and throw const in...

Class Color Const    'A const is a value; all methods are 'const'
   Method New( r:float,g:float,b:float )
   End
End

Function Update:Void( c:Color )
End

Function Main:Int()
   Update( New Color( 1,2,3 ) )
End


A const value is a value that wont (shouldn't...) be modified after it's created, ie: after its constructor returns.

The idea here is that since the compiler knows a Color is const, it doesn't have to copy the entire value when it passes the new color to Update, because it knows Update wont modify it. So it can safely pass a pointer instead, ie: faster (generally). If the color is assigned to a heap var at any time, the const value will be copied.

Actually, you could 'tune' the compiler so it would only convert const values to references if the sizeof the const value >X.

Going all the way...

   Class Color Value
      Method Red:Float() Const              'const!
      Method SetRed:Void( value:Float )   'not const!
   End

   Function Func1:Void( c:Color )
      c.SetRed( 100 )
      Print c.Red()
   End
   
   Function Func2:Void( c:Color Const )
      c.SetRed( 100 )
      Print c.Red()
   End

   Function Main:Void()
      Local c:=New Color(1,2,3 )
      Func1( c )  'value will always be copied
      Func2( c )  'pointer passed instead
   End
End


This is like 'const correct' in c++, but unlike c++ here it definitely seems to have a point(!) as it effectively auto generates whether you need to pass by value or reference.

Going full-on const correct like that last example might be a bit much, but the Value and Const modifiers to Class would be pretty useful on their own.


EdzUp(Posted 2015) [#2]
I think the main thing here is dont over complicate it otherwise it will be a nightmare to maintain, yes its loverly to have a final language that ya can build on but if it gets to complicated early on it will be harder code wise to sort problems out and obscure errors.


Danilo(Posted 2015) [#3]
Class Vec3 Value
   ...decls...
End


As I understand it, Vec3 can only be used as value then, not dynamically with pointers.
Wouldn't that be very limiting?

In C++ you can use classes with both, like:
Class Vec3
   ...decls...
End

function main:int()
   local p:Vec3()                ' local var = on stack in C++
   local q:Vec3 Ptr = New Vec3() ' pointer   = dynamically in C++

   local r:=New Vec3()           ' local could mean on stack with MX2
   local s:=New Vec3() Ptr       ' would be a pointer in MX2

end ' at end of function/method the destructor
    ' for values on stack is automatically called by C++,
    ' whereas pointers are not freed automatically.
    ' The GC in MX2 does it, or we explicitely call
    ' delete q
    ' delete s

I also consider the destructor important (for clean-up).
With local objects (values) the destructor should be called
at function/method exit. GC collected pointers should also
call a destructor or destroy() function when GC is cleaning up.

In a library you manage OS stuff (device context, HICON, HWND, etc.) yourself
and the GC does not manage those external objects, That's the reason the destructor
needs to clean-up the external (un-managed) resources.
Without destructors the unmanaged OS resources would leak, if the user is not
calling obj.destroy() himself for every object.

Not sure if I understood that correctly, but it sounds like "Class Vec3 Value"
could only be used locally then, not dynamically...?


JaviCervera(Posted 2015) [#4]
+1 for the 'Value' stuff. Would help a lot when lots of new objects are created/passed and you don't want to stress the garbage collector. For example, 3d math library.


Nobuyuki(Posted 2015) [#5]
Structs would be great.


ziggy(Posted 2015) [#6]
I would go for Structs, Value, immutable types or whatever you may call them. I have missed them in the past and also, if we ever open the door to threading, they're sometimes handy too!


Samah(Posted 2015) [#7]
@ziggy: ...if we ever open the door to threading, they're sometimes handy too!

I will be sad if it doesn't support threading. In fact, wrapping TinyThread++ would be my first task.


ziggy(Posted 2015) [#8]
@Samah: I hope threads are in too, but that also requires some deep changes to the GC which Mark hasn't mentioned anywhere


GW_(Posted 2015) [#9]
@Samah: I hope threads are in too, but that also requires some deep changes to the GC which Mark hasn't mentioned anywhere


I really like the way D-Lang does it with it's GC. Everything is built around 'Thread local storage'. All variables, even Globals, are thread local, and must be declared as 'shared' if it's going to be seen by other threads. It also has some brilliant passing mechanisms.
If Mark goes ahead with value-types/structs, that could help a lot to ease GC issues. Here is a great article about D's concurrency and how it does sharing.
Another issue is integrating with other C/C++ libraries and how the GC will work with threading. Just like with Blitzmax, I'm content to wait for the language to pass V1.0 before threading matures.


Samah(Posted 2015) [#10]
Threading is out of scope of v0.1 anyway. Same goes for pretty much all hardware interfacing. Once the language parses, translates, and compiles, we can start looking at adding the core modules.


dmaz(Posted 2015) [#11]
I disagree very much that threading is out of scope. the language should be designed around threading otherwise it will just be tacked on and hence not as useful.


nullterm(Posted 2015) [#12]
+1 for C# style Struct. Big performance win for common types like Vec3f for 3D math. Much friendly to cache performance and one less memory block for the gc to account for. I use Vec3f all over the place, but I know because it's a separate object in of itself it comes with a slight overhead cost.




Nobuyuki(Posted 2015) [#13]
Once an instance of a Struct is created, it's immutable, right? any "changes" to a field create a new one? I'm wondering just how powerful our structs could be, like for example if we can make ones similar to HLSL's built-in type primitives. Perhaps there could be convenience syntax for initializing a struct irrespective of variable names, only their order in the struct (like some have suggested for enums)....

'Any way to declare an object case-insensitive to make it comparable to other type prims would be great; 
'same for fields, actually... perhaps an Insensitive declaration parameter? Or extending Alias syntax? Something to evaluate at compile-time.

Struct Vec3   
    Field x%
    Field y%
    Field z%

'Again, there needs to be some sort of partial compile-time evaluation or something to make this the most efficient...

'Insert Property g%, Property b% here..., or have some sorta Alias syntax to evaluate these directly at compile time.
  Property r%
    Getter; Return x
    Setter; x = value 
  End Property

'Multi-field property
  Property rb%[]
    Getter;  Return [x,y]
    Setter (r,b)
      r = Self.x
      b = Self.y
  End Property
End Struct

'Insert Property rg%[], gb%[] here.....

Function Main:Int()
    Local a:Vec3 = [255,255,255]  'auto-initializer:  it populates the first 3 fields

    a.rb =   [Rnd(255), Rnd(255)]  'Mutates x and y.  You can do this in HLSL with vec3's!  I have no idea how they're actually implemented; see above for thoughts
End Function



I'm also wondering if monkey2 will be able to handle multi-field values as constant expressions at compile-time if they're structs. from what I understand, C++0x and D can handle this with constexpr and enum, respectively. "Const" means something a bit different in the VB world than the C world as I understand; it allows constants of any type as long as it can be evaluated at compile-time. The syntax in C# requires "enum" to be used for user-defined types like this, so it seems there's a distinction in C-like languages with this functionality. C# has the more convenient syntax compared to C++, with D and VB seeming to be the most convenient.
 
Const IDENTITY_MATRIX = [1,0,0,1,0,0]  'Type is inferred, Int[] or in some languages an Enum of Ints

Const Red:Vec3 = [255,0,0]  'Again using the initializer syntax.  Several of these could 'live' inside their own constant struct to effectively be a static enum, though separate enum syntax would be more convenient to not have to explicitly declare all members const.


Edit: Please note that using constexpr to implement this in monkey would require the underlying compiler to have decent C++0x support, or have to be implemented a different way on some other targets


Danilo(Posted 2015) [#14]
+1 for Struct / Structure
Struct Vec3f
    Field x:Float
    Field y:Float
    Field z:Float
End Struct

Structure Vec3f [Align VALUE] ' optional Align = structure packing, alignment of members
    x:Float
    y:Float
    z:Float
EndStructure

Structure myStruct [Extends anotherStruct] ' optional Extends for inheritance/inclusion
    x:Float
    y:Float
    z:Float
EndStructure



MikeHart(Posted 2015) [#15]
Threading is out of scope of v0.1 anyway. Same goes for pretty much all hardware interfacing.


Samah, you post like you are part of the development team or have control about the feature set. Is that so?


Samah(Posted 2015) [#16]
@MikeHart: Samah, you post like you are part of the development team or have control about the feature set. Is that so?

Not that I know of, but I'd like to be.