-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement traits #527
Comments
This is indeed related to first-class functions (#467) as that was our plan to emulate traits without implementing a complex trait solver and the complexity to the language that traits require. Your example could be written as the following once first class functions are implemented (though noir also doesn't have strings, I've mocked them here): struct Animal<T> {
new: fn(String) -> T,
name: fn(T) -> String,
noise: fn(T) -> String,
talk: fn(T),
}
/// Return an animal instance with a default `talk` method
fn make_animal<T>(new: fn(str) -> T, name: fn(T) -> str, noise: fn(T) -> str) -> Animal<T> {
Animal {
new,
name,
noise,
talk: |self| {
println!("{} says {}", self.name(), self.noise());
},
}
}
global sheep_animal = Animal {
new: |name| Sheep { name, naked: false },
name: |self| self.name,
noise: |self| if self.is_naked() {
"baaaaah?"
} else {
"baaaaah!"
},
// "default" methods are normal fields that can be initialized normally, or with a helper like make_animal
talk: |self| {
println!("{} pauses briefly... {}", self.name, self.noise());
}
}; This builds on concepts users already know (structs, struct initialization, globals), but may take some getting used to to use them in this new way. Since these are normal structs, functions using what would previously be trait bounds should take the struct as an extra parameter instead: // Rust equivalent
fn name_and_noise<T: Animal>(animal: T) -> String {
format!("{}{}", animal.name(), animal.noise())
}
// Noir equivalent (though String and format! do not exist in noir)
fn name_and_noise<T>(animal: T, instance: Animal<T>) -> String {
format!("{}{}", instance.name(animal), instance.noise(animal))
} The approach is more flexible than traits but requires users to manually pass them around. This is more explicit and possibly less confusing about what is going on under the hood, but is certainly more cumbersome to type. Using structs in this way means we can avoid the type-level programming of nudging the compiler to solve increasingly complex trait constraints and focus on the normal value-level programming that programmers are used to. Experienced trait/typeclass users will recognize this as the scrap your typeclasses approach. Another nice aspect of this approach is that it does not preclude adding traits later and it faster to implement, allowing more features to be in developers hands in the meantime. In fact there is already a PR for adding them: #468. |
Agree. Created the issue more for keeping it tracked here on GitHub while we work towards Rust-resemblence in the mid-/long-term. |
Leaving a comment to show my interest in traits. For example, I have a struct in which it would be nice to use generics for different sized integers if the end-user chooses. However, this would require a lot of boilerplate and require passing math functions for every integer size just to compute e.g |
Superseded by #2568. |
Problem
Noir currently lacks abstract interfaces for types to implement.
Solution
Implement
trait
as in Rust.Excerpt extracted from Rust by Example:
Alternatives considered
Partially related issue on enabling abstract operations via implementing first-class functions: #467
The text was updated successfully, but these errors were encountered: