-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Ferlium now supports reducers (such as fold, count, etc.) with a nice ergonomics: they work both with containers and iterators.
It would be ideal to do the same for mappers (such as map, filter, filter_map, see issues #79 and #78). However, this is more complex, as the return type depends on the input type's structure. One way to deal with that would be to implement generic associated types, somewhat similar to what Rust does.
This would allow to write a Mappable trait:
trait Mappable<Seq, B ↦ Item, MappedSeq<B>> {
fn map(Seq, (Item) -> B) -> MappedSeq<B>
}
This in turn would allow to implement this trait both on arrays:
impl<A, B> Mappable<Seq = Array<A>, B ↦ Item = A, MappedSeq<B> = Array<B>> {
fn map(a: Array<A>, f: A -> B) -> Array<B> { ... }
}
and would also work on iterators:
impl<A, B> Mappable<Self = Iterator<A>, B ↦ Item = A, MappedSeq<B> = Iterator<B>> {
fn map(it: Iterator<A>, f: A -> B) -> Iterator<B> { ... }
}
This approach would work but would require to re-implement Mappable for ever containers, which is acceptable. If we ever have negative impls (and/or specialization), a more generic approach would be possible, to use a blanket implementation for all Seq that are not at the same time Iterator.
In order to implement all functions like map, filter, filter_map, etc. with a single trait, we could define a slightly-more generic trait like this Mappable:
trait Mappable<Seq, B ↦ Item, Iter, MappedSeq<B>>
where
Iter: Iterator<Item>
{
fn to_iter(seq: S) -> Iter
fn from_iter(it: Iter) -> MappedSeq<B>
}
This would be implemented for each container, and blanket-implemented for iterators.
Then a map function could be:
fn map<Seq, B, M: Mappable<Seq, B>>(seq: Seq, f: (M::Item) -> B) -> M::MappedSeq<B> {
let it = M::to_iter(seq);
let fm_it = filter_map_iter(it, f);
M::from_iter(fm_it)
}