Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map a single field of the struct #217

Open
dima-starosud opened this issue Jun 26, 2023 · 7 comments
Open

Map a single field of the struct #217

dima-starosud opened this issue Jun 26, 2023 · 7 comments
Assignees

Comments

@dima-starosud
Copy link
Contributor

I am guessing SO is probably a better place to ask this, but my case is so trivial, that I would expect to see an example of it in README or docs.

There is a generic struct:

struct A<T> {
    a: i32,
    b: i32,
    c: String,
    ph: PhantomData<T>
}

and I want to avoid boilerplate doing the following:

fn into_unit<T>(a: A<T>) -> A<()> {
    A { a, b, c, ph:_ } = a;
    A { a, b, c, ph: PhantomData }
}

What would be the best way to do this with frunk?

Also, it would be great to add an example to the docs.

I've tried these:

  1. transmogrify doesn't work probably because types of PhantomData<T> and PhantomData<()> are different;

  2. map - need overlapping instances:

     fn a_1<T>(a: A<T>) -> A<()> {
         let a = frunk::into_generic(a).map(poly_fn![
             [T] |_q: PhantomData<T>| -> PhantomData<()> { PhantomData },
             [T] |x: T| -> x { x },
         ]);
         frunk::from_generic(a)
     }
    
  3. pluck - not sure why it doesn't work:

     fn a_2<T>(a: A<T>) -> A<()> {
         let (x, tail) = frunk::into_labelled_generic(a).pluck::<Field<_, PhantomData<T>>, _>();
    
         fn map_field<L, V>(_: Field<L, PhantomData<V>>) -> Field<L, PhantomData<()>> {
             field![L, PhantomData]
         }
    
         let hl = h_cons(map_field(x), tail);
    
         frunk::from_labelled_generic(hl)
     }
    
@dima-starosud
Copy link
Contributor Author

OK. Found the solution:

fn a_5<T>(a: A<T>) -> A<()> {
    let a = frunk::into_labelled_generic(a);
    frunk::from_labelled_generic(hlist![field![_, PhantomData::<()>], ...a].sculpt().0)
}

But is that the best way to do this?

@lloydmeta
Copy link
Owner

Thanks for putting up this question. To be honest, I don't consider this to be a trivial thing at all, so it's great that you figured it out. The final solution looks good, and I'm not sure I could come up with something better: building the toolkit is one thing, figuring out how to to "best" use it is something the community generally figures out better, so I'd recommend sending this to SO to see what smart ppl can figure out 😄

I'm actually quite amazed to see how much the compiler is actually able to "figure out" for you in your solution in that last line ... (placeholder field name type....sculpting...then into labelled generic). Since IME, the compiler optimises out all the intermediates states when run in release mode, my only thought is whether this going to cause you compile time woes if you have much more complex structs that you're planning to do this with. e.g. if need be (compiling takes long, or it can't figure things out), it could be worth trying to help it along with types..

fn a_5<T>(a: A<T>) -> A<()> {
    use frunk::labelled::chars::*;
    type ph = (p, h);
    let a = frunk::into_labelled_generic(a);
    frunk::from_labelled_generic(hlist![field![ph, PhantomData::<()>], ...a].sculpt().0)
}

@dima-starosud
Copy link
Contributor Author

Thank you! I appreciate the clarification.

There is an unstable feature type changing struct update syntax which should support this out of the box, and I thought that frunk could have this as a kind of killer feature while std one is unstable.

That reminded me a quote by Alan Kay "Simple things should be simple, complex things should be possible.". And to me mapping a single field from T to () doesn't seem to be a complex thing 🤔

Probably with overlapping instances, it could be more straightforward:

let a = frunk::into_generic(a);
let a = a.map(poly_fn![[T] |_: PhantomData<T>| PhantomData::<()>]);
frunk::from_generic(a)

And I think I saw some utility which does into/from for us, so the final could be:

with_generic(a, |a| a.map(poly_fn![|_: [T] PhantomData<T>| PhantomData::<()>]))

But I think you're right, there is a place for this in SO.

@lloydmeta
Copy link
Owner

@dima-starosud gotcha. It might make sense to have this in example or tests so that it can be checked + documented ?

@dima-starosud
Copy link
Contributor Author

Sure 👍 would be great to have this in the example folder I think.
I can make a PR if needed.

@dima-starosud
Copy link
Contributor Author

@lloydmeta Could you assign this to me?

@dima-starosud
Copy link
Contributor Author

Posted the question https://stackoverflow.com/questions/76612428/stable-analogues-of-type-changing-struct-update-syntax.

Will proceed with the ticket once get some good answers to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants