Open BGR360 opened 1 year ago
This is indeed looking like it would be tough, esp the multiple applicable impls part.
I wonder: would it help your usecase if (and I have yet to really think this through if it's feasible) there was a way to "dedupe" the types in a Coprod, just to get rid of the multiple impls issue (e.g. Coprod!(T, U, T, V)
becomes Coprod!(T, U, V)
) ?
Hm yeah I think that would be acceptable. All that matters is that the effectful computation is able to uninject
a T
, U
, or V
after the effect is handled.
EDIT: Although, that would throw a bit of a wrench into how I'm implementing Effect
on these types:
/// Marks types that are raised from effectful computations.
pub trait Effect {
/// The type that is returned back to the effectful computation by the effect handler(s).
type Output;
}
impl Effect for CNil {
type Output = CNil;
}
impl<Head, Tail> Effect for Coproduct<Head, Tail>
where
Head: Effect,
Tail: Effect,
{
type Output = Coproduct<Head::Output, Tail::Output>;
}
^ That is precisely why I have this problem in the first place (where (A, B, C, D)
can map to (T, U, T, U)
).
I have two other ideas brewing:
subset()
and use them for the embedding:fn reified_subset<Super, Sub, Indices, RemainderIndices>(
sup: Super
) -> (Result<Sub, Super::Remainder>, Indices, RemainderIndices)
where
Super: CoproductSubsetter<Sub, Indices>,
Super::Remainder: CoproductEmbedder<Super, RemainderIndices>,
Indices: Default,
RemainderIndices: Default,
{
(sup.subset(), Indices::default(), RemainderIndices::default())
}
I think this should work because I know that the indices needed to embed the mapped subset/remainder into the final output are the same as the indices that were needed to take the subset in the first place.
CoproductSubsetter
which is the RemainderIndices
(or make an extension trait like IndexedCoproductSubsetter
that has that). I think if you have a way of knowing the full HList of indices for a Coproduct, then you can build up the RemainderIndices by somehow "subtracting" from that set in the trait impls. But I'm still not too well-versed in how these indices are built up.Oh, hmmm, I thought this would work but it doesn't:
use frunk::coproduct::{CoproductEmbedder, CoproductMappable, CoproductSubsetter};
use frunk::{hlist, Coprod};
pub fn challenge<
Input,
Output,
Subset,
SubsetMapper,
SubsetMapperOutput,
RemainderMapper,
RemainderMapperOutput,
Indices,
RemainderIndices,
>(
input: Input,
subset_mapper: SubsetMapper,
remainder_mapper: RemainderMapper,
) -> Output
where
Input: CoproductSubsetter<Subset, Indices>,
Input::Remainder: CoproductEmbedder<Input, RemainderIndices>, // <--------
Subset: CoproductMappable<SubsetMapper, Output = SubsetMapperOutput>,
SubsetMapperOutput: CoproductEmbedder<Output, Indices>,
Input::Remainder: CoproductMappable<RemainderMapper, Output = RemainderMapperOutput>,
RemainderMapperOutput: CoproductEmbedder<Output, RemainderIndices>, // <--------
{
match input.subset() {
Ok(subset) => {
let subset_output = subset.map(subset_mapper);
subset_output.embed()
}
Err(remainder) => {
let remainder_output = remainder.map(remainder_mapper);
remainder_output.embed()
}
}
}
I assume this is because CoproductSubsetter
and CoproductEmbedder
have different ways of expressing the indices? I guess for this to work you'd need a way to convert Subsetter
indices into Embedder
indices.
Oh wait! I just needed one more piece!
pub fn challenge<
Input,
Output,
Subset,
SubsetMapper,
SubsetMapperOutput,
RemainderMapper,
RemainderMapperOutput,
Indices,
SubsetIndices, // <--------
RemainderIndices,
>(
input: Input,
subset_mapper: SubsetMapper,
remainder_mapper: RemainderMapper,
) -> Output
where
Input: CoproductSubsetter<Subset, Indices>,
Subset: CoproductEmbedder<Input, SubsetIndices>, // <--------
Input::Remainder: CoproductEmbedder<Input, RemainderIndices>,
Subset: CoproductMappable<SubsetMapper, Output = SubsetMapperOutput>,
SubsetMapperOutput: CoproductEmbedder<Output, SubsetIndices>, // <--------
Input::Remainder: CoproductMappable<RemainderMapper, Output = RemainderMapperOutput>,
RemainderMapperOutput: CoproductEmbedder<Output, RemainderIndices>,
{
match input.subset() {
Ok(subset) => {
let subset_output = subset.map(subset_mapper);
subset_output.embed()
}
Err(remainder) => {
let remainder_output = remainder.map(remainder_mapper);
remainder_output.embed()
}
}
}
That one works!
I have an interesting challenge:
I need to map some coproduct type
Coprod!(A, B, C, D)
toCoprod!(T, U, V, W)
. However, I have to be able to perform this mapping in two arbitrarily-partitioned steps.So I need to be able to, say, break the input into
(A, B)
and(C, D)
, then map them to(T, U)
and(V, W)
separately, then embed one of those into(T, U, V, W)
.So basically this:
Why
This is for the effect system I'm working on. When an effectful computation `a` calls another effectful computation `b`, it has the option to intercept any subset of the effects that `b` raises and re-raise the rest to the caller. So in some made-up language: ``` effect A -> T; effect B -> U; effect C -> V; effect D -> W; fn b() raises A | B | C | D { let t = raise A; let u = raise B; let v = raise C; let w = raise D; } fn a() raises C | D { try { b() } with { A => resume T, B => resume U, } /* implicitly re-raises C and D to be handled by the caller */ } ``` In Rust the effectful computations are implemented as generators where each raised effect does a `yield` of a `Coprod!(This actually works for the example I gave. This compiles just fine:
The real challenge is that
T, U, V, W
are not guaranteed to be unique types. If I make the following changes:Then it fails to perform inference on the
J
andK
indices due to multiple applicable impls:So it seems I have to somehow remember the indices from the subset operation. This wouldn't be so hard if all I need to do is re-embed the
Ok()
half. I think I could make some wrapper type that remembers the indicesI
of the originalsubset()
call and uses those to embed the partial output into the final output.But the tricky bit is that I also need to be able to re-embed the remainder half into the output type.
I've been staring at the
CoproductSubsetter
code on my screen for a couple of hours now and can't figure out how one might go about doing this. Is it possible to somehow get the "inverse" of the indices inferred for thesubset()
call?