Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
86c1643
Add Keys trait and impl for labelled hlists
lloydmeta Aug 24, 2018
f92ccf5
* Add ByKeyPlucker
lloydmeta Aug 25, 2018
8f127fc
* Add ByKeySculptor
lloydmeta Aug 25, 2018
8af677b
* Add a comment and attribute
lloydmeta Aug 25, 2018
9136cf5
* Add ZipWithKeys
lloydmeta Aug 25, 2018
523ece1
stash
lloydmeta Aug 25, 2018
4670626
borked
lloydmeta Aug 26, 2018
bb39e5f
Some slight progress, the error now shows that the values representation
lloydmeta Aug 26, 2018
75fe067
Transmorgify stash
lloydmeta Sep 1, 2018
47b76c4
Made some progress; need to define Keys and IntoUnlabelled for nested…
lloydmeta Sep 5, 2018
4735f44
Moar
lloydmeta Sep 5, 2018
00dd00a
* Compiles
lloydmeta Sep 22, 2018
e7b301f
Manual annotation works...
lloydmeta Sep 24, 2018
cd3f578
Interesting; it almost works, but only when you don't need all fields
lloydmeta Sep 24, 2018
6ad795f
more tweaking
lloydmeta Sep 24, 2018
cd6b88a
* Recursive also works
lloydmeta Sep 24, 2018
924711d
omg wtf bbq
lloydmeta Sep 24, 2018
4bfd133
holy fuck this might be forealz
lloydmeta Sep 24, 2018
a88e705
Add doc
lloydmeta Sep 24, 2018
95f4d03
bbq indeed.
lloydmeta Sep 24, 2018
70c070c
Great Scott
lloydmeta Sep 24, 2018
7cb42b6
* Removed unuseds traits and impls
lloydmeta Sep 24, 2018
7b860a5
* Move a few things around, improve docs
lloydmeta Sep 24, 2018
4cd15f8
* Add a doctest on the trait
lloydmeta Sep 24, 2018
51e4f39
* More docs
lloydmeta Sep 24, 2018
b352dda
* Remove PluckedValue wrapper
lloydmeta Sep 25, 2018
59a8b45
* Various punctuation fixes
lloydmeta Sep 25, 2018
60d5c07
One more punctuation fix.
lloydmeta Sep 25, 2018
5d09c4b
* Remove redundant println
lloydmeta Sep 25, 2018
1b2b48f
* Improve example
lloydmeta Sep 25, 2018
93aa11b
* Add a top-level test
lloydmeta Sep 25, 2018
c6b8b1f
Pretty debug print
lloydmeta Sep 26, 2018
1b8dedf
Add benches
lloydmeta Oct 2, 2018
d7b8178
* Move newly added indices to the `indices` module
lloydmeta Oct 4, 2018
5a32bae
Merge branch 'master' into feature/transmogrify-redux2
lloydmeta Oct 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,119 @@ let d_user: DeletedUser = frunk::labelled_convert_from(s_user);
let d_user: DeletedUser = frunk::transform_from(s_user);
```

##### Transmogrifying

Sometimes you need might have one data type that is "similar in shape" to another data type, but it
is similar _recursively_ (e.g. it has fields that are structs that have fields that are a superset of
the fields in the target type, so they are transformable recursively). `.transform_from` can't help you
there because it doesn't deal with recursion, but the `Transmogrifier` can help if both are `LabelledGeneric`
by `transmogrify()`ing from one to the other.

What is "transmogrifying"? In this context, it means to recursively tranform some data of type A into data
of type B, in a typesafe way, as long as A and B are "similarly-shaped". In other words, as long as B's
fields and their subfields are subsets of A's fields and their respective subfields, then A can be turned
into B.

As usual, the goal with Frunk is to do this:
* Using stable (so no specialisation, which would have been helpful, methinks)
* Typesafe
* No usage of `unsafe`

Here is an example:

```rust
#[macro_use]
extern crate frunk_core;

use frunk::labelled::Transmogrifier;

#[derive(LabelledGeneric)]
struct InternalPhoneNumber {
emergency: Option<usize>,
main: usize,
secondary: Option<usize>,
}

#[derive(LabelledGeneric)]
struct InternalAddress<'a> {
is_whitelisted: bool,
name: &'a str,
phone: InternalPhoneNumber,
}

#[derive(LabelledGeneric)]
struct InternalUser<'a> {
name: &'a str,
age: usize,
address: InternalAddress<'a>,
is_banned: bool,
}

#[derive(LabelledGeneric, PartialEq, Debug)]
struct ExternalPhoneNumber {
main: usize,
}

#[derive(LabelledGeneric, PartialEq, Debug)]
struct ExternalAddress<'a> {
name: &'a str,
phone: ExternalPhoneNumber,
}

#[derive(LabelledGeneric, PartialEq, Debug)]
struct ExternalUser<'a> {
age: usize,
address: ExternalAddress<'a>,
name: &'a str,
}

let internal_user = InternalUser {
name: "John",
age: 10,
address: InternalAddress {
is_whitelisted: true,
name: "somewhere out there",
phone: InternalPhoneNumber {
main: 1234,
secondary: None,
emergency: Some(5678),
},
},
is_banned: true,
};

/// Boilerplate-free conversion of a top-level InternalUser into an
/// ExternalUser, taking care of subfield conversions as well.
let external_user: ExternalUser = internal_user.transmogrify();

let expected_external_user = ExternalUser {
name: "John",
age: 10,
address: ExternalAddress {
name: "somewhere out there",
phone: ExternalPhoneNumber {
main: 1234,
},
}
};

assert_eq!(external_user, expected_external_user);
```

Note that as of writing, there are a couple of known limitations with `transmogrify()`,
some of which may be addressed in the future:

* If one of the fields is an identical type **and** derives `LabelledGeneric`,
the compiler will tell you that it can't "infer an index" for `transmogrify()`; this
is because `impl`s of the `Transmogrifier` trait will clash. This may or may not
change in the future (perhaps if we move to a pure procedural macro powered way of doing
things?)
* For types that contain many multiple deeply-nested fields that require `transmogfiy()`ing,
using this technique will likely increase your compile time.
* If you've balked at the the compile-time errors with `transform_from` when a transform is deemed
impossible (e.g. missing field), the errors for `transmogrify()` are worse to the degree that
recursive `transmogrify()` is required for your types.

For more information how Generic and Field work, check out their respective Rustdocs:
* [Generic](https://beachape.com/frunk/frunk_core/generic/index.html)
* [Labelled](https://beachape.com/frunk/frunk_core/labelled/index.html)
Expand Down
4 changes: 3 additions & 1 deletion benches/labelled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ extern crate frunk_core;
extern crate test;

use frunk::labelled::*;
use test::Bencher;
use std::convert::From;
use test::Bencher;

use frunk_core::labelled::chars::*;

#[derive(LabelledGeneric)]
struct NewUser<'a> {
Expand Down
Loading