//! Dispatch for bifoldable operations:
//! [`Bifoldable`](crate::classes::Bifoldable) and
//! [`RefBifoldable`](crate::classes::RefBifoldable).
//!
//! Provides the following dispatch traits and unified free functions:
//!
//! - [`BiFoldLeftDispatch`] + [`explicit::bi_fold_left`]
//! - [`BiFoldRightDispatch`] + [`explicit::bi_fold_right`]
//! - [`BiFoldMapDispatch`] + [`explicit::bi_fold_map`]
//!
//! Each routes to the appropriate trait method based on the closures' argument
//! types.
//!
//! ### Examples
//!
//! ```
//! use fp_library::{
//! 	brands::*,
//! 	functions::explicit::*,
//! };
//!
//! // bi_fold_left
//! let x: Result<i32, i32> = Ok(5);
//! let y = bi_fold_left::<RcFnBrand, ResultBrand, _, _, _, _, _>(
//! 	(|acc, e| acc - e, |acc, s| acc + s),
//! 	10,
//! 	x,
//! );
//! assert_eq!(y, 15);
//!
//! // bi_fold_right
//! let x: Result<i32, i32> = Err(3);
//! let y = bi_fold_right::<RcFnBrand, ResultBrand, _, _, _, _, _>(
//! 	(|e, acc| acc - e, |s, acc| acc + s),
//! 	10,
//! 	x,
//! );
//! assert_eq!(y, 7);
//!
//! // bi_fold_map
//! let x: Result<i32, i32> = Ok(5);
//! let y = bi_fold_map::<RcFnBrand, ResultBrand, _, _, _, _, _>(
//! 	(|e: i32| e.to_string(), |s: i32| s.to_string()),
//! 	x,
//! );
//! assert_eq!(y, "5".to_string());
//! ```

#[fp_macros::document_module]
pub(crate) mod inner {
	use {
		crate::{
			classes::{
				Bifoldable,
				LiftFn,
				Monoid,
				RefBifoldable,
			},
			dispatch::{
				Ref,
				Val,
			},
			kinds::*,
		},
		fp_macros::*,
	};

	// -- BiFoldLeftDispatch --

	/// Trait that routes a bi_fold_left operation to the appropriate type class method.
	///
	/// `(Fn(C, A) -> C, Fn(C, B) -> C)` resolves to [`Val`],
	/// `(Fn(C, &A) -> C, Fn(C, &B) -> C)` resolves to [`Ref`].
	#[document_type_parameters(
		"The lifetime of the values.",
		"The brand of the cloneable function to use.",
		"The brand of the bifoldable structure.",
		"The type of the first-position elements.",
		"The type of the second-position elements.",
		"The type of the accumulator.",
		"The container type (owned or borrowed), inferred from the argument.",
		"Dispatch marker type, inferred automatically."
	)]
	#[document_parameters("The closure tuple implementing this dispatch.")]
	pub trait BiFoldLeftDispatch<
		'a,
		FnBrand,
		Brand: Kind_266801a817966495,
		A: 'a,
		B: 'a,
		C: 'a,
		FA,
		Marker,
	> {
		/// Perform the dispatched bi_fold_left operation.
		#[document_signature]
		#[document_parameters("The initial accumulator value.", "The structure to fold.")]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_left::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|acc, e| acc - e, |acc, s| acc + s),
		/// 	10,
		/// 	x,
		/// );
		/// assert_eq!(y, 15);
		/// ```
		fn dispatch(
			self,
			z: C,
			fa: FA,
		) -> C;
	}

	/// Routes `(Fn(C, A) -> C, Fn(C, B) -> C)` closure tuples to [`Bifoldable::bi_fold_left`].
	#[document_type_parameters(
		"The lifetime.",
		"The cloneable function brand.",
		"The bifoldable brand.",
		"The first element type.",
		"The second element type.",
		"The accumulator type.",
		"The first closure type.",
		"The second closure type."
	)]
	#[document_parameters("The closure tuple.")]
	impl<'a, FnBrand, Brand, A, B, C, F, G>
		BiFoldLeftDispatch<
			'a,
			FnBrand,
			Brand,
			A,
			B,
			C,
			Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
			Val,
		> for (F, G)
	where
		Brand: Bifoldable,
		FnBrand: LiftFn + 'a,
		A: 'a + Clone,
		B: 'a + Clone,
		C: 'a,
		F: Fn(C, A) -> C + 'a,
		G: Fn(C, B) -> C + 'a,
	{
		#[document_signature]
		#[document_parameters("The initial accumulator value.", "The structure to fold.")]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_left::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|acc, e| acc - e, |acc, s| acc + s),
		/// 	10,
		/// 	x,
		/// );
		/// assert_eq!(y, 15);
		/// ```
		fn dispatch(
			self,
			z: C,
			fa: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
		) -> C {
			Brand::bi_fold_left::<FnBrand, A, B, C>(self.0, self.1, z, fa)
		}
	}

	/// Routes `(Fn(C, &A) -> C, Fn(C, &B) -> C)` closure tuples to [`RefBifoldable::ref_bi_fold_left`].
	///
	/// The container must be passed by reference (`&p`).
	#[document_type_parameters(
		"The lifetime.",
		"The borrow lifetime.",
		"The cloneable function brand.",
		"The bifoldable brand.",
		"The first element type.",
		"The second element type.",
		"The accumulator type.",
		"The first closure type.",
		"The second closure type."
	)]
	#[document_parameters("The closure tuple.")]
	impl<'a, 'b, FnBrand, Brand, A, B, C, F, G>
		BiFoldLeftDispatch<
			'a,
			FnBrand,
			Brand,
			A,
			B,
			C,
			&'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
			Ref,
		> for (F, G)
	where
		Brand: RefBifoldable,
		FnBrand: LiftFn + 'a,
		A: 'a + Clone,
		B: 'a + Clone,
		C: 'a,
		F: Fn(C, &A) -> C + 'a,
		G: Fn(C, &B) -> C + 'a,
	{
		#[document_signature]
		#[document_parameters(
			"The initial accumulator value.",
			"A reference to the structure to fold."
		)]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_left::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|acc, e: &i32| acc - *e, |acc, s: &i32| acc + *s),
		/// 	10,
		/// 	&x,
		/// );
		/// assert_eq!(y, 15);
		/// ```
		fn dispatch(
			self,
			z: C,
			fa: &'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
		) -> C {
			Brand::ref_bi_fold_left::<FnBrand, A, B, C>(self.0, self.1, z, fa)
		}
	}

	// -- BiFoldRightDispatch --

	/// Trait that routes a bi_fold_right operation to the appropriate type class method.
	///
	/// `(Fn(A, C) -> C, Fn(B, C) -> C)` resolves to [`Val`],
	/// `(Fn(&A, C) -> C, Fn(&B, C) -> C)` resolves to [`Ref`].
	#[document_type_parameters(
		"The lifetime of the values.",
		"The brand of the cloneable function to use.",
		"The brand of the bifoldable structure.",
		"The type of the first-position elements.",
		"The type of the second-position elements.",
		"The type of the accumulator.",
		"The container type (owned or borrowed), inferred from the argument.",
		"Dispatch marker type, inferred automatically."
	)]
	#[document_parameters("The closure tuple implementing this dispatch.")]
	pub trait BiFoldRightDispatch<
		'a,
		FnBrand,
		Brand: Kind_266801a817966495,
		A: 'a,
		B: 'a,
		C: 'a,
		FA,
		Marker,
	> {
		/// Perform the dispatched bi_fold_right operation.
		#[document_signature]
		#[document_parameters("The initial accumulator value.", "The structure to fold.")]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Err(3);
		/// let y = bi_fold_right::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e, acc| acc - e, |s, acc| acc + s),
		/// 	10,
		/// 	x,
		/// );
		/// assert_eq!(y, 7);
		/// ```
		fn dispatch(
			self,
			z: C,
			fa: FA,
		) -> C;
	}

	/// Routes `(Fn(A, C) -> C, Fn(B, C) -> C)` closure tuples to [`Bifoldable::bi_fold_right`].
	#[document_type_parameters(
		"The lifetime.",
		"The cloneable function brand.",
		"The bifoldable brand.",
		"The first element type.",
		"The second element type.",
		"The accumulator type.",
		"The first closure type.",
		"The second closure type."
	)]
	#[document_parameters("The closure tuple.")]
	impl<'a, FnBrand, Brand, A, B, C, F, G>
		BiFoldRightDispatch<
			'a,
			FnBrand,
			Brand,
			A,
			B,
			C,
			Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
			Val,
		> for (F, G)
	where
		Brand: Bifoldable,
		FnBrand: LiftFn + 'a,
		A: 'a + Clone,
		B: 'a + Clone,
		C: 'a,
		F: Fn(A, C) -> C + 'a,
		G: Fn(B, C) -> C + 'a,
	{
		#[document_signature]
		#[document_parameters("The initial accumulator value.", "The structure to fold.")]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Err(3);
		/// let y = bi_fold_right::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e, acc| acc - e, |s, acc| acc + s),
		/// 	10,
		/// 	x,
		/// );
		/// assert_eq!(y, 7);
		/// ```
		fn dispatch(
			self,
			z: C,
			fa: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
		) -> C {
			Brand::bi_fold_right::<FnBrand, A, B, C>(self.0, self.1, z, fa)
		}
	}

	/// Routes `(Fn(&A, C) -> C, Fn(&B, C) -> C)` closure tuples to [`RefBifoldable::ref_bi_fold_right`].
	///
	/// The container must be passed by reference (`&p`).
	#[document_type_parameters(
		"The lifetime.",
		"The borrow lifetime.",
		"The cloneable function brand.",
		"The bifoldable brand.",
		"The first element type.",
		"The second element type.",
		"The accumulator type.",
		"The first closure type.",
		"The second closure type."
	)]
	#[document_parameters("The closure tuple.")]
	impl<'a, 'b, FnBrand, Brand, A, B, C, F, G>
		BiFoldRightDispatch<
			'a,
			FnBrand,
			Brand,
			A,
			B,
			C,
			&'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
			Ref,
		> for (F, G)
	where
		Brand: RefBifoldable,
		FnBrand: LiftFn + 'a,
		A: 'a + Clone,
		B: 'a + Clone,
		C: 'a,
		F: Fn(&A, C) -> C + 'a,
		G: Fn(&B, C) -> C + 'a,
	{
		#[document_signature]
		#[document_parameters(
			"The initial accumulator value.",
			"A reference to the structure to fold."
		)]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Err(3);
		/// let y = bi_fold_right::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e: &i32, acc| acc - *e, |s: &i32, acc| acc + *s),
		/// 	10,
		/// 	&x,
		/// );
		/// assert_eq!(y, 7);
		/// ```
		fn dispatch(
			self,
			z: C,
			fa: &'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
		) -> C {
			Brand::ref_bi_fold_right::<FnBrand, A, B, C>(self.0, self.1, z, fa)
		}
	}

	// -- BiFoldMapDispatch --

	/// Trait that routes a bi_fold_map operation to the appropriate type class method.
	///
	/// `(Fn(A) -> M, Fn(B) -> M)` resolves to [`Val`],
	/// `(Fn(&A) -> M, Fn(&B) -> M)` resolves to [`Ref`].
	#[document_type_parameters(
		"The lifetime of the values.",
		"The brand of the cloneable function to use.",
		"The brand of the bifoldable structure.",
		"The type of the first-position elements.",
		"The type of the second-position elements.",
		"The monoid type.",
		"The container type (owned or borrowed), inferred from the argument.",
		"Dispatch marker type, inferred automatically."
	)]
	#[document_parameters("The closure tuple implementing this dispatch.")]
	pub trait BiFoldMapDispatch<
		'a,
		FnBrand,
		Brand: Kind_266801a817966495,
		A: 'a,
		B: 'a,
		M,
		FA,
		Marker,
	> {
		/// Perform the dispatched bi_fold_map operation.
		#[document_signature]
		#[document_parameters("The structure to fold.")]
		#[document_returns("The combined monoid value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_map::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e: i32| e.to_string(), |s: i32| s.to_string()),
		/// 	x,
		/// );
		/// assert_eq!(y, "5".to_string());
		/// ```
		fn dispatch(
			self,
			fa: FA,
		) -> M;
	}

	/// Routes `(Fn(A) -> M, Fn(B) -> M)` closure tuples to [`Bifoldable::bi_fold_map`].
	#[document_type_parameters(
		"The lifetime.",
		"The cloneable function brand.",
		"The bifoldable brand.",
		"The first element type.",
		"The second element type.",
		"The monoid type.",
		"The first closure type.",
		"The second closure type."
	)]
	#[document_parameters("The closure tuple.")]
	impl<'a, FnBrand, Brand, A, B, M, F, G>
		BiFoldMapDispatch<
			'a,
			FnBrand,
			Brand,
			A,
			B,
			M,
			Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
			Val,
		> for (F, G)
	where
		Brand: Bifoldable,
		FnBrand: LiftFn + 'a,
		A: 'a + Clone,
		B: 'a + Clone,
		M: Monoid + 'a,
		F: Fn(A) -> M + 'a,
		G: Fn(B) -> M + 'a,
	{
		#[document_signature]
		#[document_parameters("The structure to fold.")]
		#[document_returns("The combined monoid value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_map::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e: i32| e.to_string(), |s: i32| s.to_string()),
		/// 	x,
		/// );
		/// assert_eq!(y, "5".to_string());
		/// ```
		fn dispatch(
			self,
			fa: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
		) -> M {
			Brand::bi_fold_map::<FnBrand, A, B, M>(self.0, self.1, fa)
		}
	}

	/// Routes `(Fn(&A) -> M, Fn(&B) -> M)` closure tuples to [`RefBifoldable::ref_bi_fold_map`].
	///
	/// The container must be passed by reference (`&p`).
	#[document_type_parameters(
		"The lifetime.",
		"The borrow lifetime.",
		"The cloneable function brand.",
		"The bifoldable brand.",
		"The first element type.",
		"The second element type.",
		"The monoid type.",
		"The first closure type.",
		"The second closure type."
	)]
	#[document_parameters("The closure tuple.")]
	impl<'a, 'b, FnBrand, Brand, A, B, M, F, G>
		BiFoldMapDispatch<
			'a,
			FnBrand,
			Brand,
			A,
			B,
			M,
			&'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
			Ref,
		> for (F, G)
	where
		Brand: RefBifoldable,
		FnBrand: LiftFn + 'a,
		A: Clone + 'a,
		B: Clone + 'a,
		M: Monoid + 'a,
		F: Fn(&A) -> M + 'a,
		G: Fn(&B) -> M + 'a,
	{
		#[document_signature]
		#[document_parameters("A reference to the structure to fold.")]
		#[document_returns("The combined monoid value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_map::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e: &i32| e.to_string(), |s: &i32| s.to_string()),
		/// 	&x,
		/// );
		/// assert_eq!(y, "5".to_string());
		/// ```
		fn dispatch(
			self,
			fa: &'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
		) -> M {
			Brand::ref_bi_fold_map::<FnBrand, A, B, M>(self.0, self.1, fa)
		}
	}

	// -- Inference wrappers --

	/// Left-folds over a bifoldable structure, inferring the brand from the
	/// container type.
	///
	/// The `Brand` type parameter is inferred from the concrete type of `fa` via
	/// [`InferableBrand`](crate::kinds::InferableBrand_266801a817966495). `FnBrand`
	/// must still be specified explicitly.
	///
	/// For types that need an explicit brand, use
	/// [`explicit::bi_fold_left`](crate::functions::explicit::bi_fold_left).
	#[document_signature]
	///
	#[document_type_parameters(
		"The lifetime of the values.",
		"The cloneable function brand.",
		"The container type (owned or borrowed). Brand is inferred from this.",
		"The type of the first element.",
		"The type of the second element.",
		"The accumulator type.",
		"Dispatch marker type, inferred automatically."
	)]
	///
	#[document_parameters(
		"A tuple of (first fold function, second fold function).",
		"The initial accumulator value.",
		"The bifoldable value (owned for Val, borrowed for Ref)."
	)]
	///
	#[document_returns("The final accumulator value.")]
	#[document_examples]
	///
	/// ```
	/// use fp_library::{
	/// 	brands::*,
	/// 	functions::*,
	/// };
	///
	/// let x: Result<i32, i32> = Ok(5);
	/// let y = bi_fold_left::<RcFnBrand, _, _, _, _, _>((|acc, e| acc - e, |acc, s| acc + s), 10, x);
	/// assert_eq!(y, 15);
	/// ```
	pub fn bi_fold_left<'a, FnBrand, FA, A: 'a, B: 'a, C: 'a, Marker>(
		fg: impl BiFoldLeftDispatch<
			'a,
			FnBrand,
			<FA as InferableBrand_266801a817966495>::Brand,
			A,
			B,
			C,
			FA,
			Marker,
		>,
		z: C,
		fa: FA,
	) -> C
	where
		FA: InferableBrand_266801a817966495, {
		fg.dispatch(z, fa)
	}

	/// Right-folds over a bifoldable structure, inferring the brand from the
	/// container type.
	///
	/// The `Brand` type parameter is inferred from the concrete type of `fa` via
	/// [`InferableBrand`](crate::kinds::InferableBrand_266801a817966495). `FnBrand`
	/// must still be specified explicitly.
	///
	/// For types that need an explicit brand, use
	/// [`explicit::bi_fold_right`](crate::functions::explicit::bi_fold_right).
	#[document_signature]
	///
	#[document_type_parameters(
		"The lifetime of the values.",
		"The cloneable function brand.",
		"The container type (owned or borrowed). Brand is inferred from this.",
		"The type of the first element.",
		"The type of the second element.",
		"The accumulator type.",
		"Dispatch marker type, inferred automatically."
	)]
	///
	#[document_parameters(
		"A tuple of (first fold function, second fold function).",
		"The initial accumulator value.",
		"The bifoldable value (owned for Val, borrowed for Ref)."
	)]
	///
	#[document_returns("The final accumulator value.")]
	#[document_examples]
	///
	/// ```
	/// use fp_library::{
	/// 	brands::*,
	/// 	functions::*,
	/// };
	///
	/// let x: Result<i32, i32> = Err(3);
	/// let y = bi_fold_right::<RcFnBrand, _, _, _, _, _>((|e, acc| acc - e, |s, acc| acc + s), 10, x);
	/// assert_eq!(y, 7);
	/// ```
	pub fn bi_fold_right<'a, FnBrand, FA, A: 'a, B: 'a, C: 'a, Marker>(
		fg: impl BiFoldRightDispatch<
			'a,
			FnBrand,
			<FA as InferableBrand_266801a817966495>::Brand,
			A,
			B,
			C,
			FA,
			Marker,
		>,
		z: C,
		fa: FA,
	) -> C
	where
		FA: InferableBrand_266801a817966495, {
		fg.dispatch(z, fa)
	}

	/// Folds a bifoldable structure into a monoid, inferring the brand from the
	/// container type.
	///
	/// The `Brand` type parameter is inferred from the concrete type of `fa` via
	/// [`InferableBrand`](crate::kinds::InferableBrand_266801a817966495). `FnBrand`
	/// must still be specified explicitly.
	///
	/// For types that need an explicit brand, use
	/// [`explicit::bi_fold_map`](crate::functions::explicit::bi_fold_map).
	#[document_signature]
	///
	#[document_type_parameters(
		"The lifetime of the values.",
		"The cloneable function brand.",
		"The container type (owned or borrowed). Brand is inferred from this.",
		"The type of the first element.",
		"The type of the second element.",
		"The monoid type to fold into.",
		"Dispatch marker type, inferred automatically."
	)]
	///
	#[document_parameters(
		"A tuple of (first mapping function, second mapping function).",
		"The bifoldable value (owned for Val, borrowed for Ref)."
	)]
	///
	#[document_returns("The monoidal result of folding.")]
	#[document_examples]
	///
	/// ```
	/// use fp_library::{
	/// 	brands::*,
	/// 	functions::*,
	/// };
	///
	/// let x: Result<i32, i32> = Ok(5);
	/// let y = bi_fold_map::<RcFnBrand, _, _, _, _, _>(
	/// 	(|e: &i32| e.to_string(), |s: &i32| s.to_string()),
	/// 	&x,
	/// );
	/// assert_eq!(y, "5".to_string());
	/// ```
	pub fn bi_fold_map<'a, FnBrand, FA, A: 'a, B: 'a, M: Monoid + 'a, Marker>(
		fg: impl BiFoldMapDispatch<
			'a,
			FnBrand,
			<FA as InferableBrand_266801a817966495>::Brand,
			A,
			B,
			M,
			FA,
			Marker,
		>,
		fa: FA,
	) -> M
	where
		FA: InferableBrand_266801a817966495, {
		fg.dispatch(fa)
	}

	// -- Explicit dispatch free functions --

	/// Explicit dispatch functions requiring a Brand turbofish.
	///
	/// For most use cases, prefer the inference-enabled wrappers from
	/// [`functions`](crate::functions).
	pub mod explicit {
		use super::*;

		/// Folds a bifoldable structure from the left.
		///
		/// Dispatches to [`Bifoldable::bi_fold_left`] or [`RefBifoldable::ref_bi_fold_left`]
		/// based on whether the closures take owned or reference arguments.
		#[document_signature]
		#[document_type_parameters(
			"The lifetime of the values.",
			"The brand of the cloneable function to use.",
			"The brand of the bifoldable structure.",
			"The type of the first-position elements.",
			"The type of the second-position elements.",
			"The type of the accumulator.",
			"The container type (owned or borrowed), inferred from the argument.",
			"Dispatch marker type, inferred automatically."
		)]
		#[document_parameters(
			"A tuple of (first step function, second step function).",
			"The initial accumulator value.",
			"The structure to fold (owned for Val, borrowed for Ref)."
		)]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		///
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_left::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|acc, e| acc - e, |acc, s| acc + s),
		/// 	10,
		/// 	x,
		/// );
		/// assert_eq!(y, 15);
		/// ```
		pub fn bi_fold_left<
			'a,
			FnBrand,
			Brand: Kind_266801a817966495,
			A: 'a,
			B: 'a,
			C: 'a,
			FA,
			Marker,
		>(
			fg: impl BiFoldLeftDispatch<'a, FnBrand, Brand, A, B, C, FA, Marker>,
			z: C,
			fa: FA,
		) -> C {
			fg.dispatch(z, fa)
		}

		/// Folds a bifoldable structure from the right.
		///
		/// Dispatches to [`Bifoldable::bi_fold_right`] or [`RefBifoldable::ref_bi_fold_right`]
		/// based on whether the closures take owned or reference arguments.
		#[document_signature]
		#[document_type_parameters(
			"The lifetime of the values.",
			"The brand of the cloneable function to use.",
			"The brand of the bifoldable structure.",
			"The type of the first-position elements.",
			"The type of the second-position elements.",
			"The type of the accumulator.",
			"The container type (owned or borrowed), inferred from the argument.",
			"Dispatch marker type, inferred automatically."
		)]
		#[document_parameters(
			"A tuple of (first step function, second step function).",
			"The initial accumulator value.",
			"The structure to fold (owned for Val, borrowed for Ref)."
		)]
		#[document_returns("The final accumulator value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		///
		/// let x: Result<i32, i32> = Err(3);
		/// let y = bi_fold_right::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e, acc| acc - e, |s, acc| acc + s),
		/// 	10,
		/// 	x,
		/// );
		/// assert_eq!(y, 7);
		/// ```
		pub fn bi_fold_right<
			'a,
			FnBrand,
			Brand: Kind_266801a817966495,
			A: 'a,
			B: 'a,
			C: 'a,
			FA,
			Marker,
		>(
			fg: impl BiFoldRightDispatch<'a, FnBrand, Brand, A, B, C, FA, Marker>,
			z: C,
			fa: FA,
		) -> C {
			fg.dispatch(z, fa)
		}

		/// Maps elements of both types to a monoid and combines them.
		///
		/// Dispatches to [`Bifoldable::bi_fold_map`] or [`RefBifoldable::ref_bi_fold_map`]
		/// based on whether the closures take owned or reference arguments.
		#[document_signature]
		#[document_type_parameters(
			"The lifetime of the values.",
			"The brand of the cloneable function to use.",
			"The brand of the bifoldable structure.",
			"The type of the first-position elements.",
			"The type of the second-position elements.",
			"The monoid type.",
			"The container type (owned or borrowed), inferred from the argument.",
			"Dispatch marker type, inferred automatically."
		)]
		#[document_parameters(
			"A tuple of (first mapping function, second mapping function).",
			"The structure to fold (owned for Val, borrowed for Ref)."
		)]
		#[document_returns("The combined monoid value.")]
		#[document_examples]
		///
		/// ```
		/// use fp_library::{
		/// 	brands::*,
		/// 	functions::explicit::*,
		/// };
		///
		/// let x: Result<i32, i32> = Ok(5);
		/// let y = bi_fold_map::<RcFnBrand, ResultBrand, _, _, _, _, _>(
		/// 	(|e: i32| e.to_string(), |s: i32| s.to_string()),
		/// 	x,
		/// );
		/// assert_eq!(y, "5".to_string());
		/// ```
		pub fn bi_fold_map<
			'a,
			FnBrand,
			Brand: Kind_266801a817966495,
			A: 'a,
			B: 'a,
			M: Monoid + 'a,
			FA,
			Marker,
		>(
			fg: impl BiFoldMapDispatch<'a, FnBrand, Brand, A, B, M, FA, Marker>,
			fa: FA,
		) -> M {
			fg.dispatch(fa)
		}
	}
}

pub use inner::*;
