rune icon indicating copy to clipboard operation
rune copied to clipboard

Overloadable Index

Open lupan opened this issue 3 years ago • 7 comments

This is 2 different things which I have used many times in lua for more powerful dynamic object handling, simply the act of overloading what happens when you use either object.whatever and object["whatever"], I have used this many times to write dynamic message systems via internal templates to create fully runtime handled objects, this is horrible to use in rust due to fact its a lot of function calls, but in scripting langs it is quite simple and beautiful, for example:

struct template { //for generating the msg at runtime
    name: String,
    fields: Vec<(String, TypeEnum)> //name and default value
}
struct dynamic_msg {
    template: Arc<template>,
    fields: HashMap<String, TypeEnum> //name of field and inner value (this is cloned from template)
}
impl dynamic_msg {
  fn set_dynamic_field(&mut self, name: &str, new_value: TypeEnum) -> Result<()> {
      self.fields[name] = new_value; //missed out the checks for simplicity sake
      Ok(())
  }
fn get_dynamic_field(&self, name: &str) -> Result<&TypeEnum> {
      let val = self.fields.get(name); //missed out the checks for simplicity sake
      Ok(val)
  }
fn get_dynamic_field_mut(&mut self, name: &str) -> Result<&mut TypeEnum> {
      let val = self.fields.get_mut(name); //missed out the checks for simplicity sake
      Ok(val)
  }
}

in lua I usually override the index function on the userdata and make it simply run the set and get dynamic field functions, allowing me in lua to write this:

msg = create_msg("name")
msg.field = "potato"
etc etc
return msg

I realize this is possible using Object but that comes with 2 issues for me, one it doesn't handle if I try to access or create a field which isn't part of the msg, and it has to be parsed from and into the Object type, as for the dynamic struct system which is great, it requires the scripter to write the structs themselves.

p.s. this example above is purposely simple and far from what the optimized and expanded version would look like.

lupan avatar May 17 '21 14:05 lupan

I actually agree with this; but only for the fact that Objects can already access their fields both via [name] and .name, overall this being a option on the INDEX_GET/INDEX_SET protocols would be wonderful. Lua indeed allows this feature so I am quite used to it and overall it makes the code much nicer to read and write along with maintaining safety, which is what a scripting language should be.

LoopyAshy avatar Jul 05 '21 00:07 LoopyAshy

I'm not entirely following how this is different from the INDEX_SET and SET protocols (e.g. the former is object[key], the latter object.key).

Is this proposing that you should be able to do it in Rune without having to bind the protocols in a Rust module? If so, I think it would be covered by #23 since both INDEX_SET and SET would be expected to be protocols.

udoprog avatar Jul 05 '21 00:07 udoprog

I think the issue is that this example below doesn't work like it does in Lua which is usually done in Lua for more complex runtime generated "static" type behaviour by basically faking fields or running data whenever the type is used to get a field:

rust

pub struct example {
    map: HashMap<String, String>
}

impl example {
    fn get(&mut self, key: String) -> String {
        self.map.get(&key).unwrap().clone()
    }
}

let mut module = Module::new();
module.ty::<example>().expect("failed to add example struct");
module.inst_fn(Protocol::INDEX_GET, example::get).expect("failed to add INDEX_GET to example struct");
 //or: module.inst_fn(Protocol::GET, example::get).expect("failed to add GET to example struct");

rune

fn main(value) {
    //return value.a; //doesn't work
     return value["a"]; //does work
}

I know I have also done this technique before in past, so I can see the desire.

LoopyAshy avatar Jul 05 '21 00:07 LoopyAshy

Ah, right. So GET only works for specific fields. It would be plausible to add support though so that it can work as a regular instance fn, like so:

module.inst_fn(Protocol::GET, example::get);
module.inst_fn(Protocol::INDEX_GET, example::get);

It currently only works as a field function, but either its functionality could be extended to work as an instance fn or add another protocol.

udoprog avatar Jul 05 '21 01:07 udoprog

Ah, right. So GET only works for specific fields. It would be plausible to add support though so that it can work as a regular instance fn, like so:

module.inst_fn(Protocol::GET, example::get);
module.inst_fn(Protocol::INDEX_GET, example::get);

It currently only works as a field function, but either its functionality could be extended to work as an instance fn or add another protocol.

That sounds perfect to me; I have had to implement systems with a lot of different scripting languages within several projects I have worked on in the past, and out of all of them: Rune does seem like a winner (I love Rust's syntax regardless so I am bias), however I am very used to the aforementioned functionality and admittedly it's hard to go without; it makes the scripts syntax much more digestible for none-programmers I have worked with.

LoopyAshy avatar Jul 05 '21 01:07 LoopyAshy

Can the GET protocol default to INDEX_GET if the property access is not found?

schungx avatar Jul 05 '21 02:07 schungx

Can the GET protocol default to INDEX_GET if the property access is not found?

That is easily the best solution in my honest opinion. I'd attempt to contribute and add this feature myself but unfortunately I am very busy with work.

LoopyAshy avatar Jul 15 '21 02:07 LoopyAshy