'Lifting function to Option

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



Solution 1:[1]

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).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1