Enum frunk_core::coproduct::Coproduct
source · pub enum Coproduct<H, T> {
Inl(H),
Inr(T),
}
Expand description
Enum type representing a Coproduct. Think of this as a Result, but capable of supporting any arbitrary number of types instead of just 2.
To construct a Coproduct, you would typically declare a type using the Coprod!
type
macro and then use the inject
method.
Examples
use frunk_core::Coprod;
type I32Bool = Coprod!(i32, bool);
let co1 = I32Bool::inject(3);
let get_from_1a: Option<&i32> = co1.get();
let get_from_1b: Option<&bool> = co1.get();
assert_eq!(get_from_1a, Some(&3));
assert_eq!(get_from_1b, None);
RunVariants§
Inl(H)
Coproduct is either H or T, in this case, it is H
Inr(T)
Coproduct is either H or T, in this case, it is T
Implementations§
source§impl<Head, Tail> Coproduct<Head, Tail>
impl<Head, Tail> Coproduct<Head, Tail>
sourcepub fn inject<T, Index>(to_insert: T) -> Selfwhere
Self: CoprodInjector<T, Index>,
pub fn inject<T, Index>(to_insert: T) -> Selfwhere Self: CoprodInjector<T, Index>,
Instantiate a coproduct from an element.
This is generally much nicer than nested usage of Coproduct::{Inl, Inr}
.
The method uses a trick with type inference to automatically build the correct variant
according to the input type.
In standard usage, the Index
type parameter can be ignored,
as it will typically be solved for using type inference.
Rules
If the type does not appear in the coproduct, the conversion is forbidden.
If the type appears multiple times in the coproduct, type inference will fail.
Example
use frunk::Coproduct;
use frunk_core::Coprod;
type I32F32 = Coprod!(i32, f32);
// Constructing coproducts using inject:
let co1_nice: I32F32 = Coproduct::inject(1i32);
let co2_nice: I32F32 = Coproduct::inject(42f32);
// Compare this to the "hard way":
let co1_ugly: I32F32 = Coproduct::Inl(1i32);
let co2_ugly: I32F32 = Coproduct::Inr(Coproduct::Inl(42f32));
assert_eq!(co1_nice, co1_ugly);
assert_eq!(co2_nice, co2_ugly);
// Feel free to use `inject` on a type alias, or even directly on the
// `Coprod!` macro. (the latter requires wrapping the type in `<>`)
let _ = I32F32::inject(42f32);
let _ = <Coprod!(i32, f32)>::inject(42f32);
// You can also use a turbofish to specify the type of the input when
// it is ambiguous (e.g. an empty `vec![]`).
// The Index parameter should be left as `_`.
type Vi32Vf32 = Coprod!(Vec<i32>, Vec<f32>);
let _: Vi32Vf32 = Coproduct::inject::<Vec<i32>, _>(vec![]);
Runsourcepub fn get<S, Index>(&self) -> Option<&S>where
Self: CoproductSelector<S, Index>,
pub fn get<S, Index>(&self) -> Option<&S>where Self: CoproductSelector<S, Index>,
Borrow an element from a coproduct by type.
Example
use frunk_core::Coprod;
type I32F32 = Coprod!(i32, f32);
// You can let type inference find the desired type:
let co1 = I32F32::inject(42f32);
let co1_as_i32: Option<&i32> = co1.get();
let co1_as_f32: Option<&f32> = co1.get();
assert_eq!(co1_as_i32, None);
assert_eq!(co1_as_f32, Some(&42f32));
// You can also use turbofish syntax to specify the type.
// The Index parameter should be left as `_`.
let co2 = I32F32::inject(1i32);
assert_eq!(co2.get::<i32, _>(), Some(&1));
assert_eq!(co2.get::<f32, _>(), None);
Runsourcepub fn take<T, Index>(self) -> Option<T>where
Self: CoproductTaker<T, Index>,
pub fn take<T, Index>(self) -> Option<T>where Self: CoproductTaker<T, Index>,
Retrieve an element from a coproduct by type, ignoring all others.
Example
use frunk_core::Coprod;
type I32F32 = Coprod!(i32, f32);
// You can let type inference find the desired type:
let co1 = I32F32::inject(42f32);
let co1_as_i32: Option<i32> = co1.take();
let co1_as_f32: Option<f32> = co1.take();
assert_eq!(co1_as_i32, None);
assert_eq!(co1_as_f32, Some(42f32));
// You can also use turbofish syntax to specify the type.
// The Index parameter should be left as `_`.
let co2 = I32F32::inject(1i32);
assert_eq!(co2.take::<i32, _>(), Some(1));
assert_eq!(co2.take::<f32, _>(), None);
Runsourcepub fn uninject<T, Index>(
self
) -> Result<T, <Self as CoprodUninjector<T, Index>>::Remainder>where
Self: CoprodUninjector<T, Index>,
pub fn uninject<T, Index>( self ) -> Result<T, <Self as CoprodUninjector<T, Index>>::Remainder>where Self: CoprodUninjector<T, Index>,
Attempt to extract a value from a coproduct (or get the remaining possibilities).
By chaining calls to this, one can exhaustively match all variants of a coproduct.
Examples
Basic usage:
use frunk_core::Coprod;
type I32F32 = Coprod!(i32, f32);
type I32 = Coprod!(i32); // remainder after uninjecting f32
type F32 = Coprod!(f32); // remainder after uninjecting i32
let co1 = I32F32::inject(42f32);
// You can let type inference find the desired type.
let co1 = I32F32::inject(42f32);
let co1_as_i32: Result<i32, F32> = co1.uninject();
let co1_as_f32: Result<f32, I32> = co1.uninject();
assert_eq!(co1_as_i32, Err(F32::inject(42f32)));
assert_eq!(co1_as_f32, Ok(42f32));
// It is not necessary to annotate the type of the remainder:
let res: Result<i32, _> = co1.uninject();
assert!(res.is_err());
// You can also use turbofish syntax to specify the type.
// The Index parameter should be left as `_`.
let co2 = I32F32::inject(1i32);
assert_eq!(co2.uninject::<i32, _>(), Ok(1));
assert_eq!(co2.uninject::<f32, _>(), Err(I32::inject(1)));
RunChaining calls for an exhaustive match:
use frunk_core::Coprod;
type I32F32 = Coprod!(i32, f32);
// Be aware that this particular example could be
// written far more succinctly using `fold`.
fn handle_i32_f32(co: I32F32) -> &'static str {
// Remove i32 from the coproduct
let co = match co.uninject::<i32, _>() {
Ok(x) => return "integer!",
Err(co) => co,
};
// Remove f32 from the coproduct
let co = match co.uninject::<f32, _>() {
Ok(x) => return "float!",
Err(co) => co,
};
// Now co is empty
match co { /* unreachable */ }
}
assert_eq!(handle_i32_f32(I32F32::inject(3)), "integer!");
assert_eq!(handle_i32_f32(I32F32::inject(3.0)), "float!");
Runsourcepub fn subset<Targets, Indices>(
self
) -> Result<Targets, <Self as CoproductSubsetter<Targets, Indices>>::Remainder>where
Self: CoproductSubsetter<Targets, Indices>,
pub fn subset<Targets, Indices>( self ) -> Result<Targets, <Self as CoproductSubsetter<Targets, Indices>>::Remainder>where Self: CoproductSubsetter<Targets, Indices>,
Extract a subset of the possible types in a coproduct (or get the remaining possibilities)
This is basically uninject
on steroids. It lets you remove a number
of types from a coproduct at once, leaving behind the remainder in an Err
.
For instance, one can extract Coprod!(C, A)
from Coprod!(A, B, C, D)
to produce Result<Coprod!(C, A), Coprod!(B, D)>
.
Each type in the extracted subset is required to be part of the input coproduct.
Example
Basic usage:
use frunk_core::Coprod;
type I32BoolF32 = Coprod!(i32, bool, f32);
type I32F32 = Coprod!(i32, f32);
let co1 = I32BoolF32::inject(42_f32);
let co2 = I32BoolF32::inject(true);
let sub1: Result<Coprod!(i32, f32), _> = co1.subset();
let sub2: Result<Coprod!(i32, f32), _> = co2.subset();
assert!(sub1.is_ok());
assert!(sub2.is_err());
// Turbofish syntax for specifying the target subset is also supported.
// The Indices parameter should be left to type inference using `_`.
assert!(co1.subset::<Coprod!(i32, f32), _>().is_ok());
assert!(co2.subset::<Coprod!(i32, f32), _>().is_err());
// Order doesn't matter.
assert!(co1.subset::<Coprod!(f32, i32), _>().is_ok());
RunLike uninject
, subset
can be used for exhaustive matching,
with the advantage that it can remove more than one type at a time:
use frunk_core::{Coprod, hlist};
use frunk_core::coproduct::Coproduct;
fn handle_stringly_things(co: Coprod!(&'static str, String)) -> String {
co.fold(hlist![
|s| format!("&str {}", s),
|s| format!("String {}", s),
])
}
fn handle_countly_things(co: Coprod!(u32)) -> String {
co.fold(hlist![
|n| vec!["."; n as usize].concat(),
])
}
fn handle_all(co: Coprod!(String, u32, &'static str)) -> String {
// co is currently Coprod!(String, u32, &'static str)
let co = match co.subset().map(handle_stringly_things) {
Ok(s) => return s,
Err(co) => co,
};
// Now co is Coprod!(u32).
let co = match co.subset().map(handle_countly_things) {
Ok(s) => return s,
Err(co) => co,
};
// Now co is empty.
match co { /* unreachable */ }
}
assert_eq!(handle_all(Coproduct::inject("hello")), "&str hello");
assert_eq!(handle_all(Coproduct::inject(String::from("World!"))), "String World!");
assert_eq!(handle_all(Coproduct::inject(4)), "....");
Runsourcepub fn embed<Targets, Indices>(self) -> Targetswhere
Self: CoproductEmbedder<Targets, Indices>,
pub fn embed<Targets, Indices>(self) -> Targetswhere Self: CoproductEmbedder<Targets, Indices>,
Convert a coproduct into another that can hold its variants.
This converts a coproduct into another one which is capable of holding each of its types. The most well-supported use-cases (i.e. those where type inference is capable of solving for the indices) are:
- Reordering variants:
Coprod!(C, A, B) -> Coprod!(A, B, C)
- Embedding into a superset:
Coprod!(B, D) -> Coprod!(A, B, C, D, E)
- Coalescing duplicate inputs:
Coprod!(B, B, B, B) -> Coprod!(A, B, C)
and of course any combination thereof.
Rules
If any type in the input does not appear in the output, the conversion is forbidden.
If any type in the input appears multiple times in the output, type inference will fail.
All of these rules fall naturally out of its fairly simple definition, which is equivalent to:
coprod.fold(hlist![
|x| Coproduct::inject(x),
|x| Coproduct::inject(x),
...
|x| Coproduct::inject(x),
])
Example
use frunk_core::Coprod;
type I32BoolF32 = Coprod!(i32, bool, f32);
type BoolI32 = Coprod!(bool, i32);
let co = BoolI32::inject(true);
let embedded: I32BoolF32 = co.embed();
assert_eq!(embedded, I32BoolF32::inject(true));
// Turbofish syntax for specifying the output type is also supported.
// The Indices parameter should be left to type inference using `_`.
let embedded = co.embed::<I32BoolF32, _>();
assert_eq!(embedded, I32BoolF32::inject(true));
Runsourcepub fn to_ref<'a>(&'a self) -> <Self as ToRef<'a>>::Outputwhere
Self: ToRef<'a>,
pub fn to_ref<'a>(&'a self) -> <Self as ToRef<'a>>::Outputwhere Self: ToRef<'a>,
Borrow each variant of the Coproduct.
Example
Composing with subset
to match a subset of variants without
consuming the coproduct:
use frunk::Coproduct;
use frunk_core::Coprod;
let co: Coprod!(i32, bool, String) = Coproduct::inject(true);
assert!(co.to_ref().subset::<Coprod!(&bool, &String), _>().is_ok());
Runsourcepub fn to_mut<'a>(&'a mut self) -> <Self as ToMut<'a>>::Outputwhere
Self: ToMut<'a>,
pub fn to_mut<'a>(&'a mut self) -> <Self as ToMut<'a>>::Outputwhere Self: ToMut<'a>,
Borrow each variant of the Coproduct
mutably.
Example
Composing with subset
to match a subset of variants without
consuming the coproduct:
use frunk::Coproduct;
use frunk_core::Coprod;
let mut co: Coprod!(i32, bool, String) = Coproduct::inject(true);
assert!(co.to_mut().subset::<Coprod!(&mut bool, &mut String), _>().is_ok());
Runsourcepub fn fold<Output, Folder>(self, folder: Folder) -> Outputwhere
Self: CoproductFoldable<Folder, Output>,
pub fn fold<Output, Folder>(self, folder: Folder) -> Outputwhere Self: CoproductFoldable<Folder, Output>,
Use functions to transform a Coproduct into a single value.
A variety of types are supported for the Folder
argument:
- An
hlist![]
of closures (one for each type, in order). - A single closure (for a Coproduct that is homogenous).
- A single
Poly
.
Example
use frunk_core::{Coprod, hlist};
type I32F32Bool = Coprod!(i32, f32, bool);
let co1 = I32F32Bool::inject(3);
let co2 = I32F32Bool::inject(true);
let co3 = I32F32Bool::inject(42f32);
let folder = hlist![|&i| format!("int {}", i),
|&f| format!("float {}", f),
|&b| (if b { "t" } else { "f" }).to_string()];
assert_eq!(co1.to_ref().fold(folder), "int 3".to_string());
RunUsing a polymorphic function type has the advantage of not forcing you to care about the order in which you declare handlers for the types in your Coproduct.
use frunk::{Poly, Func};
use frunk_core::Coprod;
type I32F32Bool = Coprod!(i32, f32, bool);
impl Func<i32> for P {
type Output = bool;
fn call(args: i32) -> Self::Output {
args > 100
}
}
impl Func<bool> for P {
type Output = bool;
fn call(args: bool) -> Self::Output {
args
}
}
impl Func<f32> for P {
type Output = bool;
fn call(args: f32) -> Self::Output {
args > 9000f32
}
}
struct P;
let co1 = I32F32Bool::inject(3);
let folded = co1.fold(Poly(P));
Runsourcepub fn map<F>(self, mapper: F) -> <Self as CoproductMappable<F>>::Outputwhere
Self: CoproductMappable<F>,
pub fn map<F>(self, mapper: F) -> <Self as CoproductMappable<F>>::Outputwhere Self: CoproductMappable<F>,
Apply a function to each variant of a Coproduct.
The transforms some Coprod!(A, B, C, ..., E)
into some
Coprod!(T, U, V, ..., Z)
. A variety of types are supported for the
mapper argument:
- An
hlist![]
of closures (one for each variant). - A single closure (for mapping a Coproduct that is homogenous).
- A single
Poly
.
Examples
use frunk::{hlist, Coprod};
type I32F32Bool = Coprod!(i32, f32, bool);
type BoolStrU8 = Coprod!(bool, &'static str, u8);
let co1 = I32F32Bool::inject(3);
let co2 = I32F32Bool::inject(42f32);
let co3 = I32F32Bool::inject(true);
let mapper = hlist![
|n| n > 0,
|f| if f == 42f32 { "😀" } else { "🤨" },
|b| if b { 1u8 } else { 0u8 },
];
assert_eq!(co1.map(&mapper), BoolStrU8::inject(true));
assert_eq!(co2.map(&mapper), BoolStrU8::inject("😀"));
assert_eq!(co3.map(&mapper), BoolStrU8::inject(1u8));
RunUsing a polymorphic function type has the advantage of not forcing you to care about the order in which you declare handlers for the types in your Coproduct.
use frunk::{poly_fn, Coprod};
type I32F32Bool = Coprod!(i32, f32, bool);
let co1 = I32F32Bool::inject(3);
let co2 = I32F32Bool::inject(42f32);
let co3 = I32F32Bool::inject(true);
let mapper = poly_fn![
|b: bool| -> bool { !b },
|n: i32| -> i32 { n + 3 },
|f: f32| -> f32 { -f },
];
assert_eq!(co1.map(&mapper), I32F32Bool::inject(6));
assert_eq!(co2.map(&mapper), I32F32Bool::inject(-42f32));
assert_eq!(co3.map(&mapper), I32F32Bool::inject(false));
RunYou can also use a singular closure if the Coproduct variants are all the same.
use frunk::Coprod;
type IntInt = Coprod!(i32, i32);
type BoolBool = Coprod!(bool, bool);
let mapper = |n| n > 0;
let co = IntInt::Inl(42);
assert_eq!(co.map(mapper), BoolBool::Inl(true));
RunTrait Implementations§
source§impl<I, Tail> CoprodInjector<I, Here> for Coproduct<I, Tail>
impl<I, Tail> CoprodInjector<I, Here> for Coproduct<I, Tail>
source§impl<Head, I, Tail, TailIndex> CoprodInjector<I, There<TailIndex>> for Coproduct<Head, Tail>where
Tail: CoprodInjector<I, TailIndex>,
impl<Head, I, Tail, TailIndex> CoprodInjector<I, There<TailIndex>> for Coproduct<Head, Tail>where Tail: CoprodInjector<I, TailIndex>,
source§impl<Hd, Tl> CoprodUninjector<Hd, Here> for Coproduct<Hd, Tl>
impl<Hd, Tl> CoprodUninjector<Hd, Here> for Coproduct<Hd, Tl>
source§impl<Hd, Tl, T, N> CoprodUninjector<T, There<N>> for Coproduct<Hd, Tl>where
Tl: CoprodUninjector<T, N>,
impl<Hd, Tl, T, N> CoprodUninjector<T, There<N>> for Coproduct<Hd, Tl>where Tl: CoprodUninjector<T, N>,
source§impl<Head, Tail> CoproductEmbedder<Coproduct<Head, Tail>, HNil> for CNilwhere
CNil: CoproductEmbedder<Tail, HNil>,
impl<Head, Tail> CoproductEmbedder<Coproduct<Head, Tail>, HNil> for CNilwhere CNil: CoproductEmbedder<Tail, HNil>,
source§impl<Head, Tail, Out, NHead, NTail> CoproductEmbedder<Out, HCons<NHead, NTail>> for Coproduct<Head, Tail>where
Out: CoprodInjector<Head, NHead>,
Tail: CoproductEmbedder<Out, NTail>,
impl<Head, Tail, Out, NHead, NTail> CoproductEmbedder<Out, HCons<NHead, NTail>> for Coproduct<Head, Tail>where Out: CoprodInjector<Head, NHead>, Tail: CoproductEmbedder<Out, NTail>,
source§impl<F, R, FTail, CH, CTail> CoproductFoldable<HCons<F, FTail>, R> for Coproduct<CH, CTail>where
F: FnOnce(CH) -> R,
CTail: CoproductFoldable<FTail, R>,
impl<F, R, FTail, CH, CTail> CoproductFoldable<HCons<F, FTail>, R> for Coproduct<CH, CTail>where F: FnOnce(CH) -> R, CTail: CoproductFoldable<FTail, R>,
source§impl<P, R, CH, CTail> CoproductFoldable<Poly<P>, R> for Coproduct<CH, CTail>where
P: Func<CH, Output = R>,
CTail: CoproductFoldable<Poly<P>, R>,
impl<P, R, CH, CTail> CoproductFoldable<Poly<P>, R> for Coproduct<CH, CTail>where P: Func<CH, Output = R>, CTail: CoproductFoldable<Poly<P>, R>,
source§impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a HCons<F, MapperTail>> for Coproduct<CH, CTail>where
F: Fn(CH) -> R,
CTail: CoproductMappable<&'a MapperTail>,
impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a HCons<F, MapperTail>> for Coproduct<CH, CTail>where F: Fn(CH) -> R, CTail: CoproductMappable<&'a MapperTail>,
Implementation for mapping a Coproduct using a &hlist!
.
source§impl<'a, P, CH, CTail> CoproductMappable<&'a Poly<P>> for Coproduct<CH, CTail>where
P: Func<CH>,
CTail: CoproductMappable<&'a Poly<P>>,
impl<'a, P, CH, CTail> CoproductMappable<&'a Poly<P>> for Coproduct<CH, CTail>where P: Func<CH>, CTail: CoproductMappable<&'a Poly<P>>,
Implementation for mapping a Coproduct using a &poly_fn!
.
source§impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a mut HCons<F, MapperTail>> for Coproduct<CH, CTail>where
F: FnMut(CH) -> R,
CTail: CoproductMappable<&'a mut MapperTail>,
impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a mut HCons<F, MapperTail>> for Coproduct<CH, CTail>where F: FnMut(CH) -> R, CTail: CoproductMappable<&'a mut MapperTail>,
Implementation for mapping a Coproduct using a &mut hlist!
.
source§impl<'a, P, CH, CTail> CoproductMappable<&'a mut Poly<P>> for Coproduct<CH, CTail>where
P: Func<CH>,
CTail: CoproductMappable<&'a mut Poly<P>>,
impl<'a, P, CH, CTail> CoproductMappable<&'a mut Poly<P>> for Coproduct<CH, CTail>where P: Func<CH>, CTail: CoproductMappable<&'a mut Poly<P>>,
Implementation for mapping a Coproduct using a &mut poly_fn!
.
source§impl<F, R, CH, CTail> CoproductMappable<F> for Coproduct<CH, CTail>where
F: FnMut(CH) -> R,
CTail: CoproductMappable<F>,
impl<F, R, CH, CTail> CoproductMappable<F> for Coproduct<CH, CTail>where F: FnMut(CH) -> R, CTail: CoproductMappable<F>,
Implementation for mapping a Coproduct using a single function that can handle all variants.
source§impl<F, R, MapperTail, CH, CTail> CoproductMappable<HCons<F, MapperTail>> for Coproduct<CH, CTail>where
F: FnOnce(CH) -> R,
CTail: CoproductMappable<MapperTail>,
impl<F, R, MapperTail, CH, CTail> CoproductMappable<HCons<F, MapperTail>> for Coproduct<CH, CTail>where F: FnOnce(CH) -> R, CTail: CoproductMappable<MapperTail>,
Implementation for mapping a Coproduct using an hlist!
.
source§impl<P, CH, CTail> CoproductMappable<Poly<P>> for Coproduct<CH, CTail>where
P: Func<CH>,
CTail: CoproductMappable<Poly<P>>,
impl<P, CH, CTail> CoproductMappable<Poly<P>> for Coproduct<CH, CTail>where P: Func<CH>, CTail: CoproductMappable<Poly<P>>,
Implementation for mapping a Coproduct using a poly_fn!
.
source§impl<Head, FromTail, Tail, TailIndex> CoproductSelector<FromTail, There<TailIndex>> for Coproduct<Head, Tail>where
Tail: CoproductSelector<FromTail, TailIndex>,
impl<Head, FromTail, Tail, TailIndex> CoproductSelector<FromTail, There<TailIndex>> for Coproduct<Head, Tail>where Tail: CoproductSelector<FromTail, TailIndex>,
source§impl<Head, Tail> CoproductSelector<Head, Here> for Coproduct<Head, Tail>
impl<Head, Tail> CoproductSelector<Head, Here> for Coproduct<Head, Tail>
source§impl<Head, FromTail, Tail, TailIndex> CoproductTaker<FromTail, There<TailIndex>> for Coproduct<Head, Tail>where
Tail: CoproductTaker<FromTail, TailIndex>,
impl<Head, FromTail, Tail, TailIndex> CoproductTaker<FromTail, There<TailIndex>> for Coproduct<Head, Tail>where Tail: CoproductTaker<FromTail, TailIndex>,
source§impl<Head, Tail> CoproductTaker<Head, Here> for Coproduct<Head, Tail>
impl<Head, Tail> CoproductTaker<Head, Here> for Coproduct<Head, Tail>
source§impl<H: Ord, T: Ord> Ord for Coproduct<H, T>
impl<H: Ord, T: Ord> Ord for Coproduct<H, T>
1.21.0 · source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere Self: Sized,
source§impl<H: PartialEq, T: PartialEq> PartialEq for Coproduct<H, T>
impl<H: PartialEq, T: PartialEq> PartialEq for Coproduct<H, T>
source§impl<H: PartialOrd, T: PartialOrd> PartialOrd for Coproduct<H, T>
impl<H: PartialOrd, T: PartialOrd> PartialOrd for Coproduct<H, T>
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
self
and other
) and is used by the <=
operator. Read more