Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds FuncMut and PolyMut, which parallel Func and Poly. #137

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]:
- Added `ToMut` trait, which allows borrowing mutably from a Coproduct or HList.
- Added `FuncMut` and `PolyMut`, which parallel `Func` and `Poly`.

## [0.2.2] - 2018-10-21
- Added support for [transmogrifying (recursively sculpting)](https://docs.rs/frunk/0.2.2/frunk/labelled/trait.Transmogrifier.html) one data type into another
Expand Down
19 changes: 17 additions & 2 deletions core/src/coproduct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@

use hlist::{HCons, HNil};
use indices::{Here, There};
use traits::{Func, Poly, ToMut, ToRef};
use traits::{Func, FuncMut, Poly, PolyMut, ToMut, ToRef};

/// Enum type representing a Coproduct. Think of this as a Result, but capable
/// of supporting any arbitrary number of types instead of just 2.
Expand Down Expand Up @@ -506,9 +506,10 @@ impl<Head, Tail> Coproduct<Head, Tail> {
///
/// * An `hlist![]` of closures (one for each type, in order).
/// * A single closure (for a Coproduct that is homogenous).
/// * A single [`Poly`].
/// * A single [`Poly`] or [`PolyMut`].
///
/// [`Poly`]: ../traits/struct.Poly.html
/// [`PolyMut`]: ../traits/struct.PolyMut.html
///
/// # Example
///
Expand Down Expand Up @@ -745,6 +746,20 @@ where
}
}

impl<P, R, CH, CTail> CoproductFoldable<PolyMut<P>, R> for Coproduct<CH, CTail>
where
P: FuncMut<CH, Output = R>,
CTail: CoproductFoldable<PolyMut<P>, R>,
{
fn fold(self, mut f: PolyMut<P>) -> R {
use self::Coproduct::*;
match self {
Inl(r) => f.0.call(r),
Inr(rest) => rest.fold(f),
}
}
}

impl<F, R, FTail, CH, CTail> CoproductFoldable<HCons<F, FTail>, R> for Coproduct<CH, CTail>
where
F: FnOnce(CH) -> R,
Expand Down
27 changes: 21 additions & 6 deletions core/src/hlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
//! ```

use indices::{Here, Suffixed, There};
use traits::{Func, IntoReverse, Poly, ToMut, ToRef};
use traits::{Func, FuncMut, IntoReverse, Poly, PolyMut, ToMut, ToRef};

use std::ops::Add;

Expand Down Expand Up @@ -352,9 +352,10 @@ macro_rules! gen_inherent_methods {
///
/// * An `hlist![]` of closures (one for each element).
/// * A single closure (for mapping an HList that is homogenous).
/// * A single [`Poly`].
/// * A single [`Poly`] or [`PolyMut`].
///
/// [`Poly`]: ../traits/struct.Poly.html
/// [`PolyMut`]: ../traits/struct.PolyMut.html
///
/// # Examples
///
Expand Down Expand Up @@ -908,6 +909,20 @@ where
}
}

impl<P, H, Tail> HMappable<PolyMut<P>> for HCons<H, Tail>
where
P: FuncMut<H>,
Tail: HMappable<PolyMut<P>>,
{
type Output = HCons<<P as FuncMut<H>>::Output, <Tail as HMappable<PolyMut<P>>>::Output>;
fn map(self, mut poly: PolyMut<P>) -> Self::Output {
HCons {
head: poly.0.call(self.head),
tail: self.tail.map(poly),
}
}
}

/// Trait for mapping over an HList
///
/// This trait is part of the implementation of the inherent method
Expand Down Expand Up @@ -943,12 +958,12 @@ impl<F> HMappable<F> for HNil {

impl<F, R, H, Tail> HMappable<F> for HCons<H, Tail>
where
F: Fn(H) -> R,
F: FnMut(H) -> R,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for this

Tail: HMappable<F>,
{
type Output = HCons<R, <Tail as HMappable<F>>::Output>;

fn map(self, f: F) -> Self::Output {
fn map(self, mut f: F) -> Self::Output {
let HCons { head, tail } = self;
HCons {
head: f(head),
Expand Down Expand Up @@ -1143,11 +1158,11 @@ where
impl<F, H, Tail, Acc> HFoldLeftable<F, Acc> for HCons<H, Tail>
where
Tail: HFoldLeftable<F, Acc>,
F: Fn(Acc, H) -> Acc,
F: FnMut(Acc, H) -> Acc,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for this

{
type Output = <Tail as HFoldLeftable<F, Acc>>::Output;

fn foldl(self, f: F, acc: Acc) -> Self::Output {
fn foldl(self, mut f: F, acc: Acc) -> Self::Output {
let HCons { head, tail } = self;
let acc = f(acc, head);
tail.foldl(f, acc)
Expand Down
49 changes: 49 additions & 0 deletions core/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ pub trait IntoReverse {
#[derive(Debug, Copy, Clone, Default)]
pub struct Poly<T>(pub T);

/// A wrapper like [`Poly`] for [`FuncMut`].
///
/// [`FuncMut`]: trait.FuncMut.html
/// [`Poly`]: trait.Poly.html
#[derive(Debug, Copy, Clone, Default)]
pub struct PolyMut<T>(pub T);

/// This is a simple, user-implementable alternative to `Fn`.
///
/// Might not be necessary if/when Fn(Once, Mut) traits are implementable
Expand All @@ -74,3 +81,45 @@ pub trait Func<Input> {
/// use cases.
fn call(i: Input) -> Self::Output;
}

/// This is a user-implementable alternative to `FnMut`.
///
/// Not necessary if/when the FnMut trait is implementable in stable Rust.
ExpHP marked this conversation as resolved.
Show resolved Hide resolved
///
/// ```
/// # #[macro_use]
/// # extern crate frunk;
/// # use frunk::{FuncMut, PolyMut};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because some items are exported under multiple paths, I've been making it a point for doc examples to always show the intended paths for using stuff from frunk, so unhide this.

Likewise for the #[macro_use] extern crate

/// # use std::fmt::{Debug, Write};
///
/// struct ConcatDebug<'a>(&'a mut String);
///
/// impl<'a, T: Debug> FuncMut<T> for ConcatDebug<'a> {
/// type Output = ();
/// fn call(&mut self, t: T) {
/// write!(self.0, "{:?}", t).unwrap();
/// }
/// }
///
/// fn main() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide the fn main() {.

There's some examples of how I would format an example like this in
https://github.com/lloydmeta/frunk/blob/master/src/validated.rs

/// let mut string = String::new();
///
/// {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really can't wait for NLL...

/// let mut concatter = ConcatDebug(&mut string);
/// let l = hlist![
/// 23,
/// "foo",
/// (1, "two", 3.0)
/// ];
/// l.map(PolyMut(concatter));
/// }
///
/// assert_eq!(string, r#"23"foo"(1, "two", 3.0)"#);
/// }
/// ```
pub trait FuncMut<Input> {
type Output;

/// Call the `FuncMut`.
fn call(&mut self, i: Input) -> Self::Output;
}
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,9 @@ pub use hlist::HCons;
#[doc(no_inline)]
pub use hlist::HNil;
#[doc(no_inline)]
pub use traits::Func;
pub use traits::{Func, FuncMut};
#[doc(no_inline)]
pub use traits::Poly;
pub use traits::{Poly, PolyMut};
#[doc(no_inline)]
pub use traits::{ToMut, ToRef}; // useful for where bounds

Expand Down