From 0c528be2ea74f9aae1d4ac80b282ba9c16674649 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 03:39:42 +0100 Subject: Introduce `with` helper and use `sipper` in `gallery` example --- core/src/lib.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'core/src') diff --git a/core/src/lib.rs b/core/src/lib.rs index d5c221ac..ac0a228f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -93,3 +93,63 @@ pub use smol_str::SmolStr; pub fn never(never: std::convert::Infallible) -> T { match never {} } + +/// Applies the given prefix value to the provided closure and returns +/// a new closure that takes the other argument. +/// +/// This lets you partially "apply" a function—equivalent to currying, +/// but it only works with binary functions. If you want to apply an +/// arbitrary number of arguments, use the [`with!`] macro instead. +/// +/// # When is this useful? +/// Sometimes you will want to identify the source or target +/// of some message in your user interface. This can be achieved through +/// normal means by defining a closure and moving the identifier +/// inside: +/// +/// ```rust +/// # let element: Option<()> = Some(()); +/// # enum Message { ButtonPressed(u32, ()) } +/// let id = 123; +/// +/// # let _ = { +/// element.map(move |result| Message::ButtonPressed(id, result)) +/// # }; +/// ``` +/// +/// That's quite a mouthful. [`with()`] lets you write: +/// +/// ```rust +/// # use iced_core::with; +/// # let element: Option<()> = Some(()); +/// # enum Message { ButtonPressed(u32, ()) } +/// let id = 123; +/// +/// # let _ = { +/// element.map(with(Message::ButtonPressed, id)) +/// # }; +/// ``` +/// +/// Effectively creating the same closure that partially applies +/// the `id` to the message—but much more concise! +pub fn with( + mut f: impl FnMut(T, R) -> O, + prefix: T, +) -> impl FnMut(R) -> O +where + T: Clone, +{ + move |result| f(prefix.clone(), result) +} + +/// Applies the given prefix values to the provided closure in the first +/// argument and returns a new closure that takes its last argument. +/// +/// This is variadic version of [`with()`] which works with any number of +/// arguments. +#[macro_export] +macro_rules! with { + ($f:expr, $($x:expr),+ $(,)?) => { + move |result| $f($($x),+, result) + }; +} -- cgit From d2d93d09166a013924deda9f03e92887651cdbf7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 03:42:44 +0100 Subject: Fix typo in `with!` documentation --- core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/src') diff --git a/core/src/lib.rs b/core/src/lib.rs index ac0a228f..a6d4edc7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -145,7 +145,7 @@ where /// Applies the given prefix values to the provided closure in the first /// argument and returns a new closure that takes its last argument. /// -/// This is variadic version of [`with()`] which works with any number of +/// This is a variadic version of [`with()`] which works with any number of /// arguments. #[macro_export] macro_rules! with { -- cgit From 080db348495f6e92f13391d3b222c5a2769b5fc1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 03:48:49 +0100 Subject: Make `with` take a `Copy` prefix Use the `with!` macro otherwise! --- core/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core/src') diff --git a/core/src/lib.rs b/core/src/lib.rs index a6d4edc7..a5540787 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -137,9 +137,9 @@ pub fn with( prefix: T, ) -> impl FnMut(R) -> O where - T: Clone, + T: Copy, { - move |result| f(prefix.clone(), result) + move |result| f(prefix, result) } /// Applies the given prefix values to the provided closure in the first -- cgit From eab723866e1dc94ebd6d7c5c0c3ef191c80bcf59 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 10:36:45 +0100 Subject: Replace `with` function with `Function` trait --- core/src/lib.rs | 107 +++++++++++++++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 55 deletions(-) (limited to 'core/src') diff --git a/core/src/lib.rs b/core/src/lib.rs index a5540787..cd05cb25 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -94,62 +94,59 @@ pub fn never(never: std::convert::Infallible) -> T { match never {} } -/// Applies the given prefix value to the provided closure and returns -/// a new closure that takes the other argument. +/// A trait extension for binary functions (`Fn(A, B) -> O`). /// -/// This lets you partially "apply" a function—equivalent to currying, -/// but it only works with binary functions. If you want to apply an -/// arbitrary number of arguments, use the [`with!`] macro instead. -/// -/// # When is this useful? -/// Sometimes you will want to identify the source or target -/// of some message in your user interface. This can be achieved through -/// normal means by defining a closure and moving the identifier -/// inside: -/// -/// ```rust -/// # let element: Option<()> = Some(()); -/// # enum Message { ButtonPressed(u32, ()) } -/// let id = 123; -/// -/// # let _ = { -/// element.map(move |result| Message::ButtonPressed(id, result)) -/// # }; -/// ``` -/// -/// That's quite a mouthful. [`with()`] lets you write: -/// -/// ```rust -/// # use iced_core::with; -/// # let element: Option<()> = Some(()); -/// # enum Message { ButtonPressed(u32, ()) } -/// let id = 123; -/// -/// # let _ = { -/// element.map(with(Message::ButtonPressed, id)) -/// # }; -/// ``` -/// -/// Effectively creating the same closure that partially applies -/// the `id` to the message—but much more concise! -pub fn with( - mut f: impl FnMut(T, R) -> O, - prefix: T, -) -> impl FnMut(R) -> O -where - T: Copy, -{ - move |result| f(prefix, result) +/// It enables you to use a bunch of nifty functional programming paradigms +/// that work well with iced. +pub trait Function { + /// Applies the given first argument to a binary function and returns + /// a new function that takes the other argument. + /// + /// This lets you partially "apply" a function—equivalent to currying, + /// but it only works with binary functions. If you want to apply an + /// arbitrary number of arguments, create a little struct for them. + /// + /// # When is this useful? + /// Sometimes you will want to identify the source or target + /// of some message in your user interface. This can be achieved through + /// normal means by defining a closure and moving the identifier + /// inside: + /// + /// ```rust + /// # let element: Option<()> = Some(()); + /// # enum Message { ButtonPressed(u32, ()) } + /// let id = 123; + /// + /// # let _ = { + /// element.map(move |result| Message::ButtonPressed(id, result)) + /// # }; + /// ``` + /// + /// That's quite a mouthful. [`with`] lets you write: + /// + /// ```rust + /// # use iced_core::Function; + /// # let element: Option<()> = Some(()); + /// # enum Message { ButtonPressed(u32, ()) } + /// let id = 123; + /// + /// # let _ = { + /// element.map(Message::ButtonPressed.with(id)) + /// # }; + /// ``` + /// + /// Effectively creating the same closure that partially applies + /// the `id` to the message—but much more concise! + fn with(self, prefix: A) -> impl Fn(B) -> O; } -/// Applies the given prefix values to the provided closure in the first -/// argument and returns a new closure that takes its last argument. -/// -/// This is a variadic version of [`with()`] which works with any number of -/// arguments. -#[macro_export] -macro_rules! with { - ($f:expr, $($x:expr),+ $(,)?) => { - move |result| $f($($x),+, result) - }; +impl Function for F +where + F: Fn(A, B) -> O, + Self: Sized, + A: Copy, +{ + fn with(self, prefix: A) -> impl Fn(B) -> O { + move |result| self(prefix, result) + } } -- cgit From 7d99e4d07e0fa6b60b802b18312b825c9c09fe29 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 20:25:48 +0100 Subject: Use `Function` trait in `Element::map` example --- core/src/element.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'core/src') diff --git a/core/src/element.rs b/core/src/element.rs index ede9e16c..b7d51aeb 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -93,6 +93,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// /// ```no_run /// # mod iced { + /// # pub use iced_core::Function; /// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, ()>; /// # /// # pub mod widget { @@ -119,7 +120,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// use counter::Counter; /// /// use iced::widget::row; - /// use iced::Element; + /// use iced::{Element, Function}; /// /// struct ManyCounters { /// counters: Vec, @@ -142,7 +143,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// // Here we turn our `Element` into /// // an `Element` by combining the `index` and the /// // message of the `element`. - /// counter.map(move |message| Message::Counter(index, message)) + /// counter.map(Message::Counter.with(index)) /// }), /// ) /// .into() -- cgit From 6f48671042088a9cbde701f83e58667025349307 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 20:28:19 +0100 Subject: Fix broken link in `Function` documentation --- core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/src') diff --git a/core/src/lib.rs b/core/src/lib.rs index cd05cb25..03cc0632 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -122,7 +122,7 @@ pub trait Function { /// # }; /// ``` /// - /// That's quite a mouthful. [`with`] lets you write: + /// That's quite a mouthful. [`with`](Self::with) lets you write: /// /// ```rust /// # use iced_core::Function; -- cgit