Thoughts on functional trait Then for Sized type

pub trait Then<U, F: Fn(Self) -> U> where Self: Sized {
    fn then(self, fx: F) -> U;
}

impl<T: Sized, U, F: Fn(T) -> U> Then<U, F> for T {
    #[inline]
    fn then(self, fx: F) -> U {
        fx(self)
    }
}

// for demonstration
fn main() {
    (2 as u32).then(|r| assert_eq!(r, 2));
    ().then(|r| assert_eq!(r, ()));
    let mapped: u32 = (10 as u8).then(|r| r as u32);
}

It surprised me that this functional trait didn't exist for arbitrary sized types (none that I could find). Why wasn't this created in standard library? There are some cases when having this functional code-flow makes code easier to understand. E.g:

fn input<T, E>(res: Result<T, E>) -> u32 {
     res
        .map_or_else(|err: E| getSomeUErr(err), |ok: T| getSomeUOk(ok))
        .then(|u| processSomeU(u))
        .then(|u| transformation0(u))
        .then(|u| transformation1(u))
        .then(|u| transformation2(u))
}

We could easily just wrap processSomeU around the getSomeUErr or getSomeUOk functions, but ... it is indeed conceivable that the code is easier to read when using the functional style displaying the execution of function transformations one-by-one. Since then is inlined, performance shouldn't be compromised. What do y'all think?

There's an external crate that implements this:

https://crates.io/crates/pipe-trait

3 Likes

Cool. The use of pipe as the base function name is a bit weird though (I think of IPC).

Futures has then, so the use of then above would require clarification.

What would be a more proper name than then?

Perhaps, move? After all, self is being moved into a new closure

Unfortunately, move can't be used as a method name, because it is a keyword.

1 Like

Derp ...

More readable than just using let?

fn input<T, E>(res: Result<T, E>) -> u32 {
     let u = res.map_or_else(|err: E| getSomeUErr(err), |ok: T| getSomeUOk(ok));
     let u = processSomeU(u);
     let u = transformation0(u);
     let u = transformation1(u);
     transformation2(u)
}

I don't personally see a compelling reason to rewrite this as a chain of .thens.

1 Like

Any name is almost always guaranteed to have collisions. The only solution for this is an operator.