Skip to content

Commit

Permalink
generate mapX methods with macro
Browse files Browse the repository at this point in the history
  • Loading branch information
kalaninja committed Sep 2, 2023
1 parent bc04b09 commit 4ed0739
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 28 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ use rust2fun::prelude::*;

### Data types:

- non-empty vector (NEVec)
- NEVec (non-empty vector)
- Validated
- ValidatedNev

Expand Down
62 changes: 59 additions & 3 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub fn apply_ap(input: TokenStream) -> TokenStream {
let fn_name = format_ident!("ap{}", arity);
let msg = format!("Is a version of [Apply::ap] for a function of {arity} arguments.");

let types = ('A'..'Z').take(arity as usize);
let types = ('A'..='Y').take(arity as usize);
let generic_type_args = types
.clone()
.filter(|&x| x != 'B')
Expand All @@ -113,8 +113,7 @@ pub fn apply_ap(input: TokenStream) -> TokenStream {
|(curr, mut gen), t| {
let next = quote!((#curr, #t));
let next_gen = quote! {
Self::Target< #curr >: Semigroupal< #t >
+ Higher<Target< #t > = Self::Target< #t >>
Self::Target< #curr >: Apply< #t , Target< #t > = Self::Target< #t >>
+ Higher<Target< #next > = Self::Target< #next >>
};
gen.push(next_gen);
Expand Down Expand Up @@ -153,6 +152,63 @@ pub fn apply_ap(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}

#[proc_macro]
pub fn apply_map(input: TokenStream) -> TokenStream {
let arity = parse_arity(input);
let fn_name = format_ident!("map{}", arity);
let msg = format!("Is a version of [Apply::map2] for a function of {arity} arguments.");

let types = ('B'..'Z').take((arity - 1) as usize);
let generic_type_args = types.clone().skip(1).map(|t| format_ident!("{}", t));
let fn_args = types.clone().map(|t| {
let a = format_ident!("f{}", t.to_lowercase().next().unwrap());
let t = format_ident!("{}", t);
quote!(#a: Self::Target<#t>)
});
let constraints = {
let (f, mut c) = generic_type_args.clone().fold(
(quote!((Self::Param, B)), Vec::new()),
|(curr, mut gen), t| {
let next = quote!((#curr, #t));
let next_gen = quote! {
Self::Target< #curr >: Apply< #t , Target< #t > = Self::Target< #t >>
+ Higher<Target< #next > = Self::Target< #next >>
};
gen.push(next_gen);
(next, gen)
},
);

c.push(quote!(Self::Target< #f >: Functor<Z, Target<Z> = Self::Target<Z>>));
c
};
let fn_types = types.clone().map(|t| format_ident!("{}", t));
let f_args = types.clone().map(|t| format_ident!("{}", t.to_lowercase().next().unwrap()));

let products = types.clone().map(|t| {
let a = format_ident!("f{}", t.to_lowercase().next().unwrap());
quote!(product(#a))
});
let map_pattern = types
.map(|t| format_ident!("{}", t.to_lowercase().next().unwrap()))
.fold(quote!(a), |acc, a| quote!((#acc, #a)));

let expanded = quote! {
#[doc = #msg]
#[inline]
fn #fn_name< #( #generic_type_args ),* , Z, FN >( self, #( #fn_args ),* , mut func: FN) -> Self::Target<Z>
where
FN: FnMut( Self::Param, #( #fn_types ),* ) -> Z,
Self: Sized,
#( #constraints ),*
{
self. #( #products ).* .map(| #map_pattern | func(a, #( #f_args ),* ))
}
};

TokenStream::from(expanded)
}

fn parse_arity(input: TokenStream) -> u32 {
match input.into_iter().next().expect("arity is required") {
TokenTree::Literal(x) => x
Expand Down
30 changes: 18 additions & 12 deletions src/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use core::marker::PhantomData;

use rust2fun_macros::apply_ap;
use rust2fun_macros::{apply_ap, apply_map};

use crate::functor::Functor;
use crate::prelude::Higher;
Expand Down Expand Up @@ -39,8 +39,7 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
fn ap2<A, Z>(self, fa: Self::Target<A>, fb: Self::Target<B>) -> Self::Target<Z>
where
Self::Param: FnOnce(A, B) -> Z,
Self::Target<(Self::Param, B)>: Semigroupal<A>
+ Higher<Target<A> = Self::Target<A>>
Self::Target<(Self::Param, B)>: Apply<A, Target<A> = Self::Target<A>>
+ Higher<Target<((Self::Param, B), A)> = Self::Target<((Self::Param, B), A)>>,
Self::Target<((Self::Param, B), A)>: Functor<Z, Target<Z> = Self::Target<Z>>,
Self: Sized,
Expand All @@ -67,11 +66,9 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
) -> Self::Target<Z>
where
Self::Param: FnOnce(A, B, C) -> Z,
Self::Target<(Self::Param, B)>: Semigroupal<A>
+ Higher<Target<A> = Self::Target<A>>
Self::Target<(Self::Param, B)>: Apply<A, Target<A> = Self::Target<A>>
+ Higher<Target<((Self::Param, B), A)> = Self::Target<((Self::Param, B), A)>>,
Self::Target<((Self::Param, B), A)>: Semigroupal<C>
+ Higher<Target<C> = Self::Target<C>>
Self::Target<((Self::Param, B), A)>: Apply<C, Target<C> = Self::Target<C>>
+ Higher<Target<(((Self::Param, B), A), C)> = Self::Target<(((Self::Param, B), A), C)>>,
Self::Target<(((Self::Param, B), A), C)>: Functor<Z, Target<Z> = Self::Target<Z>>,
Self: Sized,
Expand Down Expand Up @@ -131,15 +128,24 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
fn map3<C, Z, F>(self, fb: Self::Target<B>, fc: Self::Target<C>, mut f: F) -> Self::Target<Z>
where
F: FnMut(Self::Param, B, C) -> Z,
Self::Target<(Self::Param, B)>: Apply<C, Target<C> = Self::Target<C>>,
Self::Target<(Self::Param, B)>: Higher<Target<Z> = Self::Target<Z>>,
<Self::Target<(Self::Param, B)> as Higher>::Target<((Self::Param, B), C)>:
Functor<Z, Target<Z> = Self::Target<Z>>,
Self::Target<(Self::Param, B)>: Apply<C, Target<C> = Self::Target<C>>
+ Higher<Target<((Self::Param, B), C)> = Self::Target<((Self::Param, B), C)>>,
Self::Target<((Self::Param, B), C)>: Functor<Z, Target<Z> = Self::Target<Z>>,
Self: Sized,
{
self.product(fb).map2(fc, |(a, b), c| f(a, b, c))
self.product(fb).product(fc).map(|((a, b), c)| f(a, b, c))
}

apply_map!(4);
apply_map!(5);
apply_map!(6);
apply_map!(7);
apply_map!(8);
apply_map!(9);
apply_map!(10);
apply_map!(11);
apply_map!(12);

/// Compose two effectful values discarding the result of the first.
///
/// # Examples
Expand Down
31 changes: 19 additions & 12 deletions src/data/ne_vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,26 @@
//! assert_eq!(nevec[2], 3);
//! ```
use core::num::NonZeroUsize;
use std::{mem, ptr, vec};
use std::ops::{Index, IndexMut};
use std::vec::Vec;
use std::{mem, ptr, vec};

use crate::{flatmap_iter, higher, invariant_functor, semigroup_extend};
use crate::applicative::Applicative;
use crate::functor::Functor;
use crate::prelude::Apply;
use crate::semigroup::Semigroup;
use crate::semigroupal::Semigroupal;
use crate::{flatmap_iter, higher, invariant_functor, semigroup_extend};

mod partial_eq;
mod iter;
mod from;
mod iter;
mod partial_eq;

/// A non-empty vector. The first element is `head`, and the remaining elements are `tail`.
/// The length of the NEVec is always at least one. The tail may be empty.
///
/// See the [module-level documentation](self) for more details.
#[allow(clippy::len_without_is_empty)]
#[derive(Clone, Debug, Eq, Hash, PartialOrd, Ord)]
pub struct NEVec<T> {
/// The first element of the NEVec, known as the head.
Expand Down Expand Up @@ -180,8 +181,8 @@ impl<T> NEVec<T> {
/// ```
#[inline]
pub fn from_elem(elem: T, n: NonZeroUsize) -> Self
where
T: Clone,
where
T: Clone,
{
Self {
head: elem.clone(),
Expand Down Expand Up @@ -230,7 +231,10 @@ impl<T> NEVec<T> {
/// assert_eq!(NEVec::<bool>::from_slice(&[]), None);
/// ```
#[inline]
pub fn from_slice(slice: &[T]) -> Option<Self> where T: Clone {
pub fn from_slice(slice: &[T]) -> Option<Self>
where
T: Clone,
{
slice.split_first().map(|(h, t)| Self {
head: h.clone(),
tail: t.to_vec(),
Expand Down Expand Up @@ -564,7 +568,10 @@ impl<T> NEVec<T> {
/// assert_eq!(nevec, [1, 2, 3]);
/// ```
#[inline]
pub fn to_vec(&self) -> Vec<T> where T: Clone {
pub fn to_vec(&self) -> Vec<T>
where
T: Clone,
{
let mut vec = Vec::with_capacity(self.len());
vec.push(self.head.clone());
vec.extend_from_slice(&self.tail);
Expand Down Expand Up @@ -602,7 +609,7 @@ impl<T: Default> Default for NEVec<T> {

impl<T> Extend<T> for NEVec<T> {
#[inline]
fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.tail.extend(iter);
}
}
Expand Down Expand Up @@ -721,8 +728,8 @@ impl<A, B> Semigroupal<B> for NEVec<A> {
impl<F, B> Apply<B> for NEVec<F> {
#[inline]
fn ap<A>(self, fa: NEVec<A>) -> NEVec<B>
where
Self::Param: FnOnce(A) -> B,
where
Self::Param: FnOnce(A) -> B,
{
NEVec {
head: (self.head)(fa.head),
Expand All @@ -736,4 +743,4 @@ impl<T> Applicative for NEVec<T> {
fn pure(x: T) -> Self {
NEVec::new(x)
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#![no_std]
#![deny(missing_docs)]
#![allow(clippy::too_many_arguments)]

extern crate rust2fun_macros;
#[cfg(feature = "std")]
Expand Down

0 comments on commit 4ed0739

Please sign in to comment.