The idea
// Def
trait DoSomething {
fn do_something();
}
mod foo : DoSomething {
fn do_something() { ... }
}
mod bar : DoSomething {
fn do_something() { ... }
}
mod baz {
a: DoSomething;
b: DoSomething;
fn do_something() {
self::b::do_something();
self::c::do_something();
}
}
// Usage
baz::<a=foo, b=bar>::do_something();
Why?
I prefer to separate business logic from data. (It is because I did some functional programming in the past. E.g., That's what elixir does.) Currently, rust does the opposite. We bind methods to data which makes everything stateful. If I want to follow this idea in rust, the code became unmanageable quickly, especially when I want to make it generic enough for unit tests. The above idea ease this by allowing modules to use other modules dynamically so that I could use modules instead of structs.
Why Modules?
I don't want state.
State is crucial in programming, but most of the Request -> Response subsystems can be implemented without state. So I would definitely use this for REST APIs.
And that's a completely different world. Without states, some of the rust rules could be simplified.
Why Composable?
- For tests:
baz::<a=fake_foo, b=fake_bar>::do_something();
- For different functionality:
crud_controller::<repo=user_repo>::list::<User>::();
crud_controller::<repo=order_repo>::list::<Order>::();
Let's play with the idea.
EDIT
A bit more specific description
-
Modules could define module vars, and everything could be accessible through module vars. e.g.:
foo
has a module varbar
, thus insidefoo
we can access everything throughself::bar::
syntax. -
To ensure the elements in a module, there would be a need for module traits, which is an ordinary trait. But we could use them for modules, e.g.:
mod foo: SomeTrait + SomeOtherTrait
. -
Defining Module vars:
mod foo {
some_name: SomeTrait;
some_other_name: SomeOtherTrait;
// ...
}
-
The usage would be similar to the named type arguments
baz::<a=foo, b=bar>::do_something();
. -
Interpretation: for compiler, this would work like generics, so:
// this would define a `do_something` function which would use `bar`
foo::<a=bar>::do_something();
// this would define another `do_something` function which would use `baz`
foo::<a=baz>::do_something();
How this could help?
Instead of this:
struct Baz<A, B>(std::marker::PhantomData<(A, B)>);
impl<A: DoSomething, B: DoSomething> DoSomething for Baz<A, B> {
fn do_something() {
A::do_something();
B::do_something();
}
}
Or instead of this:
struct Baz<A, B>{
a: A,
b: B
);
impl<A: DoSomething, B: DoSomething> DoSomething for Baz<A, B> {
fn do_something() {
A::do_something();
B::do_something();
}
}
we would be able to write this:
mod baz {
a: DoSomething;
b: DoSomething;
fn do_something() {
self::b::do_something();
self::c::do_something();
}
}