Lifting function to Option

538 Views Asked by At

Is there a way to lift a simple function, like this

fn add(a:i32, b:i32) -> i32 {a+b}

to operate on Option (or any other monadic type), similar to how one would use Applicative in Haskell

I'm aware of this solution:

pub fn add(a: Option<i32>, b: Option<i32>) -> Option<i32> {
  Some(a? + b?)
}

but that requires me to actually write a separate function, that is tightly coupled with Option, whereas what I want is to be able to lift an arbitrary function to an arbitrary monadic type, or some other way to re-use functions operating on simple types with arguments and return values of monadic types

// something like this
let func = Option::lift2(add) //example, not working code                         

I'm obviously thinking Haskell, maybe there's more idiomatic way of doing this in Rust

1

There are 1 best solutions below

5
phimuemue On

You could start out with this:

fn lift<A, B, C>(f: impl Fn(A, B)->C) -> impl Fn(Option<A>, Option<B>)->Option<C> {
    move |oa, ob| {
        match (oa, ob) {
            (Some(a), Some(b)) => Some(f(a,b)),
            _ => None,
        }
    }
}

Or, to make it shorter:

fn lift<A, B, C>(f: impl Fn(A, B)->C) -> impl Fn(Option<A>, Option<B>)->Option<C> {
    move |a, b| Some(f(a?, b?))
}

Beware that you would possibly need similar things for FnMut and FnOnce, too.

Moreover, the above is still bound to Option, and not generic over monadic things in general, which is afaik quite cumbersome to simulate in Rust (if possible at all).