rune
rune copied to clipboard
Use dynamically dispatched generics to reduce namespace contamination
Come up with a generics scheme that works at runtime and allows for:
- Dynamically dispatching a function call to the correct implementation depending on the type of the arguments.
- Directly address the exact generic implementation at compile-time using generic function-call syntax (e.g.
std::parse::<i64>(string)
).
Suggested implementation details
The idea is that native generic functions are monomorphized at registration time. Take:
str::parse::<int>("42");
"1,2,3,4".split::<char>(',');
"1,2,3,4".split::<str>("2,3"); // Note: different argument type, different function impl is needed because its a different kind of pattern.
It would actually look up a function named "parse", who's hash includes the int
type constructor.
Programmatically this would be something like:
// NB: this is the regular parse function hash.
let parse_fn_hash = Hash::type_hash(&["parse"]);
Hash::type_hash_generic(parse_fn_hash, (i64::type_hash(),))
So what happens if we call the function generically?
This will error, since there's no generic type information available. We don't know which str::parse
impl to pick:
str::parse("42");
The above case has to be specified at compile time, because the type of the argument doesn't determine the implementation to use:
str::parse::<i64>("42");
For case where we can look at the type of the argument (like String::split
), the generic function by first looking up the split
instance fn, then using the metadata to resolve all generic parameters and notice that they are input arguments, so it can generate the necessary type hash:
"1,2,3,4".split(',');
Or can be used more explicitly to avoid the dynamic dispatch:
"1,2,3,4".split::<char>(',');
This would probably also improve situations where one wants to support more than one type and currently needs to accept Value
:
impl Path {
// should support `Path` and `String`
fn eq(&self, path: Value) -> VmResult<bool> {
let path = match path {
string @ (Value::StaticString(_) | Value::String(_)) => {
Path::from_str(match String::from_value(string) {
VmResult::Ok(o) => o,
VmResult::Err(e) => return VmResult::Err(e),
})
}
any @ Value::Any(_) => match <Ref<Path>>::from_value(any) {
VmResult::Ok(o) => o.to_owned(),
VmResult::Err(e) => return VmResult::Err(e),
},
other => {
return VmResult::panic(format!(
"Expected `Path` or `String` type, but found `{}`",
unwrap!(other.type_info())
));
}
};
VmResult::Ok(self == &path)
}
}