YetAnotherForum
Welcome Guest Search | Active Topics | Log In | Register

[Squirrel 2.1 PLAN] class Properties
fagiano
#1 Posted : Friday, July 15, 2005 4:08:29 AM(UTC)
Rank: Advanced Member

Groups: Registered, Administrators
Joined: 6/11/2005(UTC)
Posts: 1,059

Thanks: 0 times
Was thanked: 79 time(s) in 61 post(s)
I'd like to add properties with setter and getters to classes. I had 2 ideas so far, both have some aspect that I don't really like.


Option A. C#-ish setter and getter

<PRE>class Stuff {
function DoThings() {
}
//////////////////////////////
thestuff = 1;
///////////////////////////////
Stuff {
set {
validate_things(value);
thestuff = value;
}
get {
return thestuff;
}
}
///////////////////////////////
ReadOnlyProp {
get {
return 25;
}
}
}</PRE>
This looks fine but I don't know what to return as value when the class(not an instance) is iterated with foreach. That should be solved before implementing this option.


Option B. Special function names. If a function is prefixed with '_get_' becomes a property getter , same thing for the setter. When the method is added the class stores it in a special way and the method wouldn't be accessible as normal method anymore.

<PRE>class Stuff {
function DoThings() {
}
///////////////////////////////////
thestuff = 1;
///////////////////////////////////
function _set_Stuff(value) {
validate_things(value);
thestuff = value;
}
function _get_Stuff()
return thestuff;
}
///////////////////////////////////
function _get_ReadOnlyProp() {
return 25;
}
}</PRE>
This option would be less painful to implement than the first and wont add additional keywords/bloat to the language. The only remaining problem is regarding attributes. There is only one property but the syntax would allow to set 2 distinct attributes(one per function). Is a bit inconsistent, I don't know what to return in class.getattributes("Stuff").


Suggestions are welcome.


ciao


Alberto

Follow me on Twitter @squirrellang
aradesign
#2 Posted : Friday, July 15, 2005 9:53:26 AM(UTC)
Rank: Member

Groups: Registered
Joined: 7/10/2005(UTC)
Posts: 23

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
Hello,

I like the first (C# C++) style but in form You post this is not very clean.

I suggest some syntax change (if possible):

Code:

class Stuff {
function DoThings() {
}
//////////////////////////////
thestuff = 1;
///////////////////////////////
property Stuff {
  set(value) {
   validate_things(value);
   thestuff = value;
  }
  get {
   return thestuff;
  }
}
///////////////////////////////
property ReadOnlyProp {
  get {
   return 25;
  }
}
///////////////////////////////
property WriteOnlyProp {
  set(v) {
   thestuff = v;
  }
}
}



Another possible syntax is:
Code:

class Stuff {
function DoThings() {
}
//////////////////////////////
thestuff = 1;
///////////////////////////////
  property set Stuff(value) {
   validate_things(value);
   thestuff = value;
}
  property get Stuff {
   return thestuff;
}
///////////////////////////////
property get ReadOnlyProp {
   return 25;
}
// this is disable set even if inherited object have set func, not required
property set null ReadOnlyProp;
///////////////////////////////
// this is disable get even if inherited object have get func, not required
property get null ReadOnlyProp;
property set WriteOnlyProp(v) {
   thestuff = v;
}
}


Regards.
JesusChrist
#3 Posted : Monday, July 18, 2005 6:48:16 PM(UTC)
Rank: Member

Groups: Registered
Joined: 6/27/2005(UTC)
Posts: 6

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
Hi,



Well the C# way is better IMO.



Make a property type, that is basically a table with the value, the set and get methods.



When you iterate just enumerate it as 'property', which allows you to get it's name, default value, set and get methods.



Code:


table/class Stuff <- {

  Thingy = property {

     value = 1

     function set(value) {

       ...

     }

     function get() {

       return value

     }

   }



   // syntaxics sugar (same as function)

   property Fagiano {

       value = 2

       function set(v) {

         ...

       }

       function get(v) {

       }

   }

}





This probably could already be done by just implementing the _set and _get metamethod, and get the property 'table' and call the set/get.



Anyway the point is to have something more lightweight that a table I suppose ;)



- Pierre


caecus314
#4 Posted : Friday, June 19, 2015 9:46:43 PM(UTC)
Rank: Newbie

Groups: Registered
Joined: 9/20/2014(UTC)
Posts: 5
Location: San Francisco

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
Hey, I hope you don't mind that I'm reviving such an ancient thread, but I'm curious whether anything ever resulted from this discussion. I really like the way that C# does properties, particularly their auto-implemented properties, because of how much boilerplate code they can save. On the other hand, I can certainly see how implementing a similar language-level feature in a scripting language could be difficult. Are there any future plans to add properties to Squirrel? If not, could you explain why you decided against it? Thank you!
fagiano
#5 Posted : Saturday, June 20, 2015 1:12:24 PM(UTC)
Rank: Advanced Member

Groups: Registered, Administrators
Joined: 6/11/2005(UTC)
Posts: 1,059

Thanks: 0 times
Was thanked: 79 time(s) in 61 post(s)
No worries about the old post thing, you can always ask :)
At the moment I have no plans to add properties. It boils down to the complexity, performance penalty and how they would fit in the current VM design.
The major thing that turns me off is that property setters would behave more like a metamethod than a "set" operation. When you write "object.something = x" or "object.somthing()" the VM can infer from the syntax what you are trying to do. In the first case is a "set" in the secod case is a "call", so it generates the appropriate instruction that is lean and fast.
A "set" operation works by hashing the class/table and then, only if the key is missing, it will invoke a metamethod "_set" as a fallback. Metamethods introduce some limitations compared to a vanilla call(they cannot be suspended and they use extra stack space). Properties cannot be inferred, to execute "object.something = x" the VM would have to figure out if it is a property at runtime. The setter function for ".something" would have to be invoked like a metamethod as a form of fallback. This would kind of end up being more like an extension than a "builtin" feature. That is what bothers me. Also things like enumerating members of a class would have to be rethought as properties woulndn't fit the current model.

The following shows how you could go about implementing properties in pure squirrel, I whipped up the following code in about an hour so is not the most tested thing in the world and it could be improved. (NOTE: you cannot fallback to the root table from a setter/getter you must use :: in fromnt of external values)
This also give you an idea on what the VM would have to do internally.

Code:

// Author      : alberto
// Create Date : 6/20/2015 7:47:47 PM

local function defaultsetter(v) { throw null; }
local function defaultgetter() { throw null; }
local dummygettersetter = { get = defaultgetter, set = defaultsetter }
local defaultdelegate = { function _get(key) { return dummygettersetter; } }
class Propertizator {
    static _props = {}
    
    function _inherited(attributes)
    {
        local newprops = clone this._props;
        foreach(pname,p in newprops) //this is to get base. work
        {
            local newproptable = clone p;
            newprops[pname] = newproptable
            foreach(i,val in p.internal_names)
            {
                newproptable[i] = this.rawget(val);
            }
        }
        this.rawnewmember("_props",newprops); //we make a clone of the base table
        
        newprops.setdelegate(defaultdelegate);
    }
    
    function _get(key)
    {
        return (this._props[key].get)();
    }    
    function _set(key,value)
    {
        return (this._props[key].set)(value);
    }
    
    function _newmember(index,value,attributes,isstatic)
    {
        if(attributes && type(value) == "table" && ("property" in attributes) && attributes.property == true)
        {
            local setter = "set" in  value ? value.set : null;
            local getter = "get" in  value ? value.get : null;
            if(setter == null && getter == null)
            {
                throw "properties must have at least a set or get function"
            }
            local settername;
            local setterfunc;
            local proptable
            if(_props.rawin(index)) {
                proptable =  _props.rawget(index);
            }
            else {
                proptable = { internal_names = {}}
            }
            if(setter) {
                local classfuncname = "__setter__" + index;
                proptable.internal_names.set <- classfuncname;
                this.rawnewmember(classfuncname,value.set,null,null); //this so base.something will work inside setters
                proptable.set <- this[classfuncname];
            }
            else {
                proptable.set <- function(v) {
                    throw index + " property has no setter";
                };
            }
            if(getter) {
                local classfuncname = "__getter__" + index;
                proptable.internal_names.get <- classfuncname;
                this.rawnewmember(classfuncname,value.get,null,null); //this so base.something will work inside getter
                
                proptable.get <- this[classfuncname];
            }
            else {
                proptable.get <- function() {
                    throw index + " property has no getter";
                };
            }
            _props[index] <- proptable
        }
        else {
            this.rawnewmember(index,value,attributes,isstatic);
        }
    }    
}


that gets used like this

Code:

class TestProperties extends Propertizator
{
    _testy = 55;
    </ property = true />
    Testy = {
        function get() {
            ::print("Testy was fetched\n");
            return _testy;
        }
        function set(val) {
            ::print("Testy was set\n");
            return _testy = val;
        }
    }
    _nosetter = 22;
    </ property = true />
    NoSetter = {
        function get() {
            ::print("NoSetter fetched\n");
            return _nosetter;
        }
    }
    _nogetter = 22;
    </ property = true />
    NoGetter = {
        function set(val) {
            ::print("NoGetter was set\n");
            return _nogetter = val;
        }
    }
}

class Inherited extends TestProperties
{
    _newprop = 666;
    </ property = true />
    NewProp = {
        function get() {
            ::print("NewProp was fetched\n");
            return _newprop;
        }
        function set(val) {
            ::print("NewProp was set\n");
            return _newprop = val;
        }
    }
    //here I add a getter to NoGetter to show inheritance
    </ property = true />
    NoGetter = {
        function get() {
            ::print("NoGetter was fetched from inherited property\n");
            return _nogetter;
        }
        function set(v) {
            //here we invoke the inherited  sowmhat verbose but it works
            ::print("inherited setter\n");
            local setter = base._props.NoGetter.set;
            return setter(v);
        }
    }
}

local a = TestProperties();
local b = Inherited();


b.Testy = 75;
b.NoGetter = 666;


print("NoSetter = " + b.NoSetter + "\n");
print("Testy = " + b.Testy + "\n");
b.NewProp = 123;
print("NewProp = " + b.NewProp + "\n");
print("NoGetter(with added getter) = " + b.NoGetter + "\n");
//print(a.NoGetter); //this fails as expected


I hope this helps

ciao
Alberto
Follow me on Twitter @squirrellang
Zylann
#6 Posted : Thursday, October 1, 2015 9:45:08 PM(UTC)
Rank: Advanced Member

Groups: Registered
Joined: 6/25/2014(UTC)
Posts: 61
Location: France

Thanks: 1 times
Was thanked: 2 time(s) in 2 post(s)
I was going to implement properties a bit like that too, but with a naming convention instead:
Quote:
function get_stuff()
function set_stuff()

But... would the "in" keyword or a foreach over the members work with properties defined this way?
fagiano
#7 Posted : Friday, October 2, 2015 5:37:09 PM(UTC)
Rank: Advanced Member

Groups: Registered, Administrators
Joined: 6/11/2005(UTC)
Posts: 1,059

Thanks: 0 times
Was thanked: 79 time(s) in 61 post(s)
"in" should work, an iterating members no. However, could be implemented using _nexti, but then you'd have to iterate the instance and not the class.
Follow me on Twitter @squirrellang
Users browsing this topic
Guest (2)
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

Clean Slate theme by Jaben Cargman (Tiny Gecko)
Powered by YAF 1.9.4 | YAF © 2003-2010, Yet Another Forum.NET
This page was generated in 0.126 seconds.