Type Definition lending_iterator::higher_kinded_types::CanonicalHKT
source · [−]pub type CanonicalHKT<T: ?Sized + HKT> = HKT<dyn for<'ඞ> WithLifetime<'ඞ, T = Feed<'ඞ, T>>>;
Expand description
[eta-expansion] Projects an arbitrary impl HKT
to its
HKT!
“canonical” (η-expanded) form.
-
To illustrate, let’s consider a non-canonical
impl HKT
type:use ::lending_iterator::higher_kinded_types::*; enum StrRef {} impl<'lt> WithLifetime<'lt> for StrRef { type T = &'lt str; }
Then, we have
StrRef : HKT
(and for any'lt
,Apply!(StrRef<'lt>) = &'lt str
).And yet,
StrRef ≠ HKT!(&str)
, since the latter is actually something along the lines ofdyn for<'lt> WithLifetime<'lt, T = &'lt str>
, which is clearly not, nominally, ourStrRef
type.This
CanonicalHKT
operation then represents an operation which “extracts” the inherentHKT
semantics of the givenimpl HKT
type (e.g.,<'n> => &'n str
for bothStrRef
andHKT!(&str)
), to then wrap them into / apply them to / project them to aHKT!
type (e.g.,HKT!(&str)
).So, while
StrRef ≠ HKT!(&str)
, we do haveCanonicalHKT<StrRef> = HKT!(&str)
👌
It’s a projection, in the mathematical sense, since the operation is
idempotent: for any T : HKT
,
CanonicalHKT<CanonicalHKT<T>> = CanonicalHKT<T>
Proof:
CanonicalHKT<T> = HKT!(hkt-ness of T)
;CanonicalHKT<U = HKT!(…)> = HKT!(hkt-ness of HKT!(…)) = HKT!(…) = U
.- Replace with
U = CanonicalHKT<T>
.
Thence the usefulness of this tool: given a generic Item : HKT
, certain
“round-tripping” operations such as going from LendingIterator
to
dyn LendingIteratorDyn
“and back” is unlikely to have kept
the very same HKT type in place: it may itself have “suffered” from a
CanonicalHKT
lift-up by such process.
Thus, APIs expecting to work with such things may avoid compile errors by
preventively CanonicalHKT
-lifting their own Item : HKT
types in the
signatures… 😅
Example
-
use ::lending_iterator::prelude::*; fn unify<'usability, I, J, Item> (i: I, j: J) -> [Box<dyn 'usability + LendingIteratorDyn<Item = CanonicalHKT<Item>>>; 2] // ^^^^^^^^^^^^^ ^ // without it, this snippet would fail to compile. where Item : HKT, I : 'usability + LendingIterator, J : 'usability + LendingIterator, // Extra bounds required for the `dyn` coercion: I : LendingIteratorDyn<Item = CanonicalHKT<Item>>, J : LendingIteratorDyn<Item = CanonicalHKT<Item>>, { [ i.dyn_boxed_auto(), j.dyn_boxed_auto(), ] } // Uncomment this to make the above function fail. // type CanonicalHKT<T> = T;
If we un-comment the above CanonicalHKT
alias which shadows it with a
no-op (i.e., if we remove the CanonicalHKT
s from the snippet above
altogether), we get the following error message:
error[E0308]: mismatched types --> src/higher_kinded_types.rs:300:9 | 9 | fn unify<'usability, I, J, Item> (i: I, j: J) | ---- this type parameter ... 21 | i.dyn_boxed(), | ^^^^^^^^^^^^^ expected type parameter `Item`, found enum `lending_iterator::HKT` | = note: expected struct `Box<(dyn LendingIteratorDyn<Item = Item> + 'usability)>` found struct `Box<dyn LendingIteratorDyn<Item = lending_iterator::HKT<(dyn for<'ඞ> WithLifetime<'ඞ, for<'ඞ> T = <Item as WithLifetime<'ඞ>>::T> + 'static)>>>`
Mostly, notice the mismatch with Item
:
lending_iterator::HKT<(dyn for<'ඞ> WithLifetime<'ඞ, /* for<'ඞ> */ T = <Item as WithLifetime<'ඞ>>::T> + 'static)>
// i.e.
lending_iterator::HKT<(dyn for<'n> WithLifetime<'n, T = Apply!(Item<'n>)>)>
// i.e.
HKT!(<'n> => Apply!(Item<'n>))
// i.e.
CanonicalHKT<Item>
Contrary to the generic Item
which may be of any shape, these types are
HKT!-constructed impl HKT
types, hence the type mismatch.
But if we lift Item
so that it be, itself, an HKT!
-constructed
impl HKT
type, that is, if we use
CanonicalHKT<Item>
rather than Item
, we no longer are in
that situation and thus avoid the issue.