functional-programming rust

functional programming - ¿Cómo componer funciones en Rust?



functional-programming (2)

Como @ljedrz señala , para que funcione solo necesita hacer referencia a las funciones compuestas nuevamente:

let finally = compose(&*add_and_multiply, &*divide_and_subtract);

(Tenga en cuenta que en Rust, la convención dicta que los nombres de las variables deben estar en snake_case)

Sin embargo, podemos mejorar esto.

Desde Rust 1.26, podemos usar tipos de retorno abstractos (previamente destacados como #![feature(conservative_impl_trait)] ). Esto puede ayudarlo a simplificar enormemente su ejemplo, ya que le permite omitir las vidas, referencias, restricciones de Sized y cuadros:

fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C where F: Fn(A) -> B, G: Fn(B) -> C, { move |x| g(f(x)) } fn main() { let add_and_multiply = compose(|x| x * 2, |x| x + 2); let divide_and_subtract = compose(|x| x / 2, |x| x - 2); let finally = compose(add_and_multiply, divide_and_subtract); println!("Result is {}", finally(10)); }

Finalmente, dado que usted menciona los parámetros de reposo, sospecho que lo que realmente quiere es tener una forma de componer en cadena tantas funciones como desee de manera flexible. Escribí esta macro para este propósito:

macro_rules! compose { ( $last:expr ) => { $last }; ( $head:expr, $($tail:expr), +) => { compose_two($head, compose!($($tail),+)) }; } fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C where F: Fn(A) -> B, G: Fn(B) -> C, { move |x| g(f(x)) } fn main() { let add = |x| x + 2; let multiply = |x| x * 2; let divide = |x| x / 2; let intermediate = compose!(add, multiply, divide); let subtract = |x| x - 2; let finally = compose!(intermediate, subtract); println!("Result is {}", finally(10)); }

Estoy tratando de escribir una función que compone dos funciones. El diseño inicial es bastante simple: una función que toma dos funciones y devuelve una función compuesta que luego puedo componer con otras funciones, ya que Rust no tiene parámetros de reposo. Me he encontrado con un muro construido con frustrantes errores de compilación no útiles.

Mi función de redacción:

fn compose<''a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + ''a> where F: ''a + Fn(A) -> B + Sized, G: ''a + Fn(B) -> C + Sized, { Box::new(move |x| g(f(x))) }

Cómo me gustaría usarlo:

fn main() { let addAndMultiply = compose(|x| x * 2, |x| x + 2); let divideAndSubtract = compose(|x| x / 2, |x| x - 2); let finally = compose(*addAndMultiply, *divideAndSubtract); println!("Result is {}", finally(10)); }

Al compilador no le gusta eso, no importa lo que intente, los límites del rasgo nunca se satisfacen. El error es:

error[E0277]: the size for values of type `dyn std::ops::Fn(_) -> _` cannot be known at compilation time --> src/main.rs:13:19 | 13 | let finally = compose(*addAndMultiply, *divideAndSubtract); | ^^^^^^^ doesn''t have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `dyn std::ops::Fn(_) -> _` = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait> note: required by `compose` --> src/main.rs:1:1 | 1 | / fn compose<''a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + ''a> 2 | | where 3 | | F: ''a + Fn(A) -> B + Sized, 4 | | G: ''a + Fn(B) -> C + Sized, 5 | | { 6 | | Box::new(move |x| g(f(x))) 7 | | } | |_^


Simplemente agregue referencias finally y funcionará:

fn main() { let addAndMultiply = compose(|x| x * 2, |x| x + 2); let divideAndSubtract = compose(|x| x / 2, |x| x - 2); let finally = compose(&*addAndMultiply, &*divideAndSubtract); println!("Result is {}", finally(10)); }

Al desreferenciar addAndMultiply o divideAndSubtract descubre un objeto de rasgo que no tiene el Sized ; necesita estar envuelto en un Box o referenciado para que se pase a una función con una restricción de Sized .