{"id":13682687,"url":"https://github.com/lloydmeta/frunk","last_synced_at":"2025-05-12T13:23:40.227Z","repository":{"id":37318651,"uuid":"71159954","full_name":"lloydmeta/frunk","owner":"lloydmeta","description":"Funktional generic type-level programming in Rust: HList, Coproduct, Generic, LabelledGeneric, Validated, Monoid and friends.","archived":false,"fork":false,"pushed_at":"2025-03-06T10:53:54.000Z","size":12486,"stargazers_count":1341,"open_issues_count":35,"forks_count":59,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-05-06T10:14:21.790Z","etag":null,"topics":["coproduct","datatype-generic-programming","fp","generic","generic-programming","hlist","labelled-generic","lenses","rust","type-level","type-level-programming","validated"],"latest_commit_sha":null,"homepage":"https://beachape.com/frunk/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lloydmeta.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-10-17T16:43:47.000Z","updated_at":"2025-05-05T08:31:24.000Z","dependencies_parsed_at":"2024-05-01T21:12:24.287Z","dependency_job_id":"1bb18658-b582-41b3-97e1-558ad24a6090","html_url":"https://github.com/lloydmeta/frunk","commit_stats":{"total_commits":291,"total_committers":28,"mean_commits":"10.392857142857142","dds":0.5876288659793815,"last_synced_commit":"1ca9c995b966ef204accd6b992401acfec302500"},"previous_names":["lloydmeta/frust"],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydmeta%2Ffrunk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydmeta%2Ffrunk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydmeta%2Ffrunk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydmeta%2Ffrunk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lloydmeta","download_url":"https://codeload.github.com/lloydmeta/frunk/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253745457,"owners_count":21957374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coproduct","datatype-generic-programming","fp","generic","generic-programming","hlist","labelled-generic","lenses","rust","type-level","type-level-programming","validated"],"created_at":"2024-08-02T13:01:51.188Z","updated_at":"2025-05-12T13:23:40.199Z","avatar_url":"https://github.com/lloydmeta.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Frunk [![Crates.io](https://img.shields.io/crates/v/frunk.svg)](https://crates.io/crates/frunk) [![Continuous integration](https://github.com/lloydmeta/frunk/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/lloydmeta/frunk/actions/workflows/ci.yml?query=branch%3Amaster) [![Gitter](https://badges.gitter.im/lloydmeta/frunk.svg)](https://gitter.im/lloydmeta/frunk?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge) [![Frunk](https://docs.rs/frunk/badge.svg)](https://docs.rs/frunk)\n\n\u003e **frunk** *frəNGk*\n\u003e  * Functional programming toolbelt in Rust.\n\u003e  * Might seem funky at first, but you'll like it.\n\u003e  * Comes from: funktional (German) + Rust → Frunk\n\nThe general idea is to make things easier by providing FP tools in Rust to allow for stuff like this:\n\n```rust\nuse frunk::monoid::combine_all;\n\nlet v = vec![Some(1), Some(3)];\nassert_eq!(combine_all(\u0026v), Some(4));\n\n// Slightly more magical\nlet t1 =       (1, 2.5f32,                String::from(\"hi\"),  Some(3));\nlet t2 =       (1, 2.5f32,            String::from(\" world\"),     None);\nlet t3 =       (1, 2.5f32,         String::from(\", goodbye\"), Some(10));\nlet tuples = vec![t1, t2, t3];\n\nlet expected = (3, 7.5f32, String::from(\"hi world, goodbye\"), Some(13));\nassert_eq!(combine_all(\u0026tuples), expected);\n```\n\nFor a deep dive, RustDocs are available for:\n* Code on [Master](https://beachape.com/frunk)\n* Latest [published release](https://docs.rs/frunk)\n\n## Table of Contents\n\n1. [HList](#hlist)\n2. [Generic](#generic)\n    * 2.1 [LabelledGeneric](#labelledgeneric)\n       * 2.1.2 [Path (Lenses)](#path)\n3. [Coproduct](#coproduct)\n4. [Validated](#validated)\n5. [Semigroup](#semigroup)\n6. [Monoid](#monoid)\n7. [Features](#features)\n8. [Benchmarks](#benchmarks)\n9. [Todo](#todo)\n10. [Contributing](#contributing)\n11. [Inspirations](#inspirations)\n12. [Maintainers](#maintainers)\n\n## Examples\n\n### HList\n\nStatically typed heterogeneous lists.\n\nFirst, let's enable `hlist`:\n```rust\nuse frunk::{HNil, HCons, hlist};\n```\n\nSome basics:\n```rust\nlet h = hlist![1];\n// Type annotations for HList are optional. Here we let the compiler infer it for us\n// h has a static type of: HCons\u003ci32, HNil\u003e\n\n// HLists have a head and tail\nassert_eq!(hlist![1].head, 1);\nassert_eq!(hlist![1].tail, HNil);\n\n// You can convert a tuple to an HList and vice-versa\nlet h2 = hlist![ 42f32, true, \"hello\" ];\nlet t: (f32, bool, \u0026str) = h2.into();\nassert_eq!(t, (42f32, true, \"hello\"));\n\nlet t3 = (999, false, \"world\");\nlet h3: HList![ isize, bool, \u0026str ] = t3.into();\nassert_eq!(h3, hlist![ 999, false, \"world\" ]);\n```\n\nHLists have a `hlist_pat!` macro for pattern matching;\n```rust\nlet h: HList!(\u0026str, \u0026str, i32, bool) = hlist![\"Joe\", \"Blow\", 30, true];\n// We use the HList! type macro to make it easier to write\n// a type signature for HLists, which is a series of nested HCons\n// h has an expanded static type of: HCons\u003c\u0026str, HCons\u003c\u0026str, HCons\u003ci32, HCons\u003cbool, HNil\u003e\u003e\u003e\u003e\n\nlet hlist_pat!(f_name, l_name, age, is_admin) = h;\nassert_eq!(f_name, \"Joe\");\nassert_eq!(l_name, \"Blow\");\nassert_eq!(age, 30);\nassert_eq!(is_admin, true);\n\n// You can also use into_tuple2() to turn the hlist into a nested pair\n```\n\nTo traverse or build lists, you can also prepend/or pop elements at the front:\n```rust\nlet list = hlist![true, \"hello\", Some(41)];\n// h has a static type of: HCons\u003cbool, HCons\u003c\u0026str, HCons\u003cOption\u003c{integer}\u003e, HNil\u003e\u003e\u003e\nlet (head1, tail1) = list.pop();\nassert_eq!(head1, true);\nassert_eq!(tail1, hlist![\"hello\", Some(41)]);\nlet list1 = tail1.prepend(head1);\nassert_eq!(list, list1);\n\n// or using macro sugar:\nlet hlist_pat![head2, ...tail2] = list; // equivalent to pop\nlet list2 = hlist![head2, ...tail2];    // equivalent to prepend\nassert_eq!(list, list2);\n```\n\nYou can reverse, map, and fold over them too:\n\n```rust\n// Reverse\nlet h1 = hlist![true, \"hi\"];\nassert_eq!(h1.into_reverse(), hlist![\"hi\", true]);\n\n// Fold (foldl and foldr exist)\nlet h2 = hlist![1, false, 42f32];\nlet folded = h2.foldr(\n    hlist![\n        |acc, i| i + acc,\n        |acc, _| if acc \u003e 42f32 { 9000 } else { 0 },\n        |acc, f| f + acc\n    ],\n    1f32\n);\nassert_eq!(folded, 9001)\n\n// Map\nlet h3 = hlist![9000, \"joe\", 41f32];\nlet mapped = h3.map(hlist![\n    |n| n + 1,\n    |s| s,\n    |f| f + 1f32]);\nassert_eq!(mapped, hlist![9001, \"joe\", 42f32]);\n```\n\nYou can pluck a type out of an `HList` using `pluck()`, which also gives you back the remainder after plucking that type\nout. This method is checked at compile-time to make sure that the type you ask for *can* be extracted.\n\n```rust\nlet h = hlist![1, \"hello\", true, 42f32];\nlet (t, remainder): (bool, _) = h.pluck();\nassert!(t);\nassert_eq!(remainder, hlist![1, \"hello\", 42f32])\n```\n\nSimilarly, you can re-shape, or sculpt, an `Hlist`, there is a `sculpt()` method, which allows you to re-organise and/or\ncull the elements by type. Like `pluck()`, `sculpt()` gives you back your target with the remainder data in a pair. This\nmethod is also checked at compile time to make sure that it won't fail at runtime (the types in your requested target shape\nmust be a subset of the types in the original `HList`.\n\n```rust\nlet h = hlist![9000, \"joe\", 41f32, true];\nlet (reshaped, remainder): (HList![f32, i32, \u0026str], _) = h.sculpt();\nassert_eq!(reshaped, hlist![41f32, 9000, \"joe\"]);\nassert_eq!(remainder, hlist![true]);\n```\n\n### Generic\n\n`Generic` is a way of representing a type in ... a generic way. By coding around `Generic`, you can to write functions\nthat abstract over types and arity, but still have the ability to recover your original type afterwards. This can be a fairly powerful thing.\n\n#### Setup\n\nIn order to derive the trait `Generic` (or `LabelledGeneric`) you will have to add `frunk_core` dependency\n\n```toml\n[dependencies]\nfrunk_core = { version = \"$version\" }\n```\n\nFrunk comes out of the box with a nice custom `Generic` derivation so that boilerplate is kept to a minimum.\n\nHere are some examples:\n\n#### HList ⇄ Struct\n\n```rust\n#[derive(Generic, Debug, PartialEq)]\nstruct Person\u003c'a\u003e {\n    first_name: \u0026'a str,\n    last_name: \u0026'a str,\n    age: usize,\n}\n\nlet h = hlist!(\"Joe\", \"Blow\", 30);\nlet p: Person = frunk::from_generic(h);\nassert_eq!(p,\n           Person {\n               first_name: \"Joe\",\n               last_name: \"Blow\",\n               age: 30,\n           });\n```\n\nThis also works the other way too; just pass a struct to `into_generic` and get its generic representation.\n\n#### Converting between Structs\n\nSometimes you may have 2 different types that are structurally the same (e.g. different domains but the same data). Use cases include:\n\n * You have a models for deserialising from an external API and equivalents for your app logic\n * You want to represent different stages of the same data using types (see [this question on StackOverflow](http://stackoverflow.com/questions/31949455/transform-one-case-class-into-another-when-the-argument-list-is-the-same))\n\nGeneric comes with a handy `convert_from` method that helps make this painless:\n\n```rust\n// Assume we have all the imports needed\n#[derive(Generic)]\nstruct ApiPerson\u003c'a\u003e {\n    FirstName: \u0026'a str,\n    LastName: \u0026'a str,\n    Age: usize,\n}\n\n#[derive(Generic)]\nstruct DomainPerson\u003c'a\u003e {\n    first_name: \u0026'a str,\n    last_name: \u0026'a str,\n    age: usize,\n}\n\nlet a_person = ApiPerson {\n                   FirstName: \"Joe\",\n                   LastName: \"Blow\",\n                   Age: 30,\n};\nlet d_person: DomainPerson = frunk::convert_from(a_person); // done\n```\n\n#### LabelledGeneric\n\nIn addition to `Generic`, there is also `LabelledGeneric`, which, as the name implies, relies on a generic representation\nthat is _labelled_. This means that if two structs derive `LabelledGeneric`, you can convert between them only if their\nfield names match!\n\nHere's an example:\n\n```rust\n// Suppose that again, we have different User types representing the same data\n// in different stages in our application logic.\n\n#[derive(LabelledGeneric)]\nstruct NewUser\u003c'a\u003e {\n    first_name: \u0026'a str,\n    last_name: \u0026'a str,\n    age: usize,\n}\n\n#[derive(LabelledGeneric)]\nstruct SavedUser\u003c'a\u003e {\n    first_name: \u0026'a str,\n    last_name: \u0026'a str,\n    age: usize,\n}\n\nlet n_user = NewUser {\n    first_name: \"Joe\",\n    last_name: \"Blow\",\n    age: 30\n};\n\n// Convert from a NewUser to a Saved using LabelledGeneric\n//\n// This will fail if the fields of the types converted to and from do not\n// have the same names or do not line up properly :)\n//\n// Also note that we're using a helper method to avoid having to use universal\n// function call syntax\nlet s_user: SavedUser = frunk::labelled_convert_from(n_user);\n\nassert_eq!(s_user.first_name, \"Joe\");\nassert_eq!(s_user.last_name, \"Blow\");\nassert_eq!(s_user.age, 30);\n\n// Uh-oh ! last_name and first_name have been flipped!\n#[derive(LabelledGeneric)]\nstruct DeletedUser\u003c'a\u003e {\n    last_name: \u0026'a str,\n    first_name: \u0026'a str,\n    age: usize,\n}\n\n//  This would fail at compile time :)\nlet d_user: DeletedUser = frunk::labelled_convert_from(s_user);\n\n// This will, however, work, because we make use of the Sculptor type-class\n// to type-safely reshape the representations to align/match each other.\nlet d_user: DeletedUser = frunk::transform_from(s_user);\n```\n\n##### Transmogrifying\n\nSometimes you need might have one data type that is \"similar in shape\" to another data type, but it\nis similar _recursively_ (e.g. it has fields that are structs that have fields that are a superset of\nthe fields in the target type, so they are transformable recursively).  `.transform_from` can't help you\nthere because it doesn't deal with recursion, but the `Transmogrifier` can help if both are `LabelledGeneric`\nby `transmogrify()`ing from one to the other.\n\nWhat is \"transmogrifying\"? In this context, it means to recursively tranform some data of type A into data\nof type B, in a typesafe way, as long as A and B are \"similarly-shaped\".  In other words, as long as B's\nfields and their subfields are subsets of A's fields and their respective subfields, then A can be turned\ninto B.\n\nAs usual, the goal with Frunk is to do this:\n* Using stable (so no specialisation, which would have been helpful, methinks)\n* Typesafe\n* No usage of `unsafe`\n\nHere is an example:\n\n```rust\nuse frunk::labelled::Transmogrifier;\n\n#[derive(LabelledGeneric)]\nstruct InternalPhoneNumber {\n    emergency: Option\u003cusize\u003e,\n    main: usize,\n    secondary: Option\u003cusize\u003e,\n}\n\n#[derive(LabelledGeneric)]\nstruct InternalAddress\u003c'a\u003e {\n    is_whitelisted: bool,\n    name: \u0026'a str,\n    phone: InternalPhoneNumber,\n}\n\n#[derive(LabelledGeneric)]\nstruct InternalUser\u003c'a\u003e {\n    name: \u0026'a str,\n    age: usize,\n    address: InternalAddress\u003c'a\u003e,\n    is_banned: bool,\n}\n\n#[derive(LabelledGeneric, PartialEq, Debug)]\nstruct ExternalPhoneNumber {\n    main: usize,\n}\n\n#[derive(LabelledGeneric, PartialEq, Debug)]\nstruct ExternalAddress\u003c'a\u003e {\n    name: \u0026'a str,\n    phone: ExternalPhoneNumber,\n}\n\n#[derive(LabelledGeneric, PartialEq, Debug)]\nstruct ExternalUser\u003c'a\u003e {\n    age: usize,\n    address: ExternalAddress\u003c'a\u003e,\n    name: \u0026'a str,\n}\n\nlet internal_user = InternalUser {\n    name: \"John\",\n    age: 10,\n    address: InternalAddress {\n        is_whitelisted: true,\n        name: \"somewhere out there\",\n        phone: InternalPhoneNumber {\n            main: 1234,\n            secondary: None,\n            emergency: Some(5678),\n        },\n    },\n    is_banned: true,\n};\n\n/// Boilerplate-free conversion of a top-level InternalUser into an\n/// ExternalUser, taking care of subfield conversions as well.\nlet external_user: ExternalUser = internal_user.transmogrify();\n\nlet expected_external_user = ExternalUser {\n    name: \"John\",\n    age: 10,\n    address: ExternalAddress {\n        name: \"somewhere out there\",\n        phone: ExternalPhoneNumber {\n            main: 1234,\n        },\n    }\n};\n\nassert_eq!(external_user, expected_external_user);\n```\n\nNote that as of writing, there are a couple of known limitations with `transmogrify()`,\nsome of which may be addressed in the future:\n\n* If one of the fields is an identical type **and** derives `LabelledGeneric`,\n  the compiler will tell you that it can't \"infer an index\" for `transmogrify()`; this\n  is because `impl`s of the `Transmogrifier` trait will clash. This may or may not\n  change in the future (perhaps if we move to a pure procedural macro powered way of doing\n  things?)\n* For types that contain many multiple deeply-nested fields that require `transmogfiy()`ing,\n  using this technique will likely increase your compile time.\n* If you've balked at the the compile-time errors with `transform_from` when a transform is deemed\n  impossible (e.g. missing field), the errors for `transmogrify()` are worse to the degree that\n  recursive `transmogrify()` is required for your types.\n\nFor more information how Generic and Field work, check out their respective Rustdocs:\n  * [Generic](https://beachape.com/frunk/frunk_core/generic/index.html)\n  * [Labelled](https://beachape.com/frunk/frunk_core/labelled/index.html)\n\n#### Path\n\nOne of the other things that `LabelledGeneric`-deriving structs can do is be generically traversed\nusing `Path` and its companion trait `PathTraverser`. In some circles, this functionality is also\ncalled a Lens.\n\n`Path`-based traversals are\n* Easy to use through the procedural macro `path!` (`frunk_proc_macros`)\n  * Traversing multiple levels is familiar; just use dot `.` syntax (`path!(nested.attribute.value)`)\n* Compile-time safe\n* Composable (add one to the other using `+`)\n* Allows you to get by value, get by reference or get by mutable reference, depending on the type\n  of thing you pass it.\n\n```rust\n#[derive(LabelledGeneric)]\nstruct Dog\u003c'a\u003e {\n    name: \u0026'a str,\n    dimensions: Dimensions,\n}\n\n#[derive(LabelledGeneric)]\nstruct Cat\u003c'a\u003e {\n    name: \u0026'a str,\n    dimensions: Dimensions,\n}\n\n#[derive(LabelledGeneric)]\nstruct Dimensions {\n    height: usize,\n    width: usize,\n    unit: SizeUnit,\n}\n\n#[derive(Debug, Eq, PartialEq)]\nenum SizeUnit {\n    Cm,\n    Inch,\n}\n\nlet mut dog = Dog {\n    name: \"Joe\",\n    dimensions: Dimensions {\n        height: 10,\n        width: 5,\n        unit: SizeUnit::Inch,\n    },\n};\n\nlet cat = Cat {\n    name: \"Schmoe\",\n    dimensions: Dimensions {\n        height: 7,\n        width: 3,\n        unit: SizeUnit::Cm,\n    },\n};\n\n// generic, re-usable, compsable paths\nlet dimensions_lens = path!(dimensions);\nlet height_lens = dimensions_lens + path!(height); // compose multiple\nlet unit_lens = path!(dimensions.unit); // dot syntax to just do the whole thing at once\n\nassert_eq!(*height_lens.get(\u0026dog), 10);\nassert_eq!(*height_lens.get(\u0026cat), 7);\nassert_eq!(*unit_lens.get(\u0026dog), SizeUnit::Inch);\nassert_eq!(*unit_lens.get(\u0026cat), SizeUnit::Cm);\n\n// modify by passing a \u0026mut\n*height_lens.get(\u0026mut dog) = 13;\nassert_eq!(*height_lens.get(\u0026dog), 13);\n```\n\nThere's also a `Path!` type-level macro for declaring shape-constraints. This allows you to write adhoc shape-dependent\nfunctions for `LabelledGeneric` types.\n\n```rust\n// Prints height as long as `A` has the right \"shape\" (e.g.\n// has `dimensions.height: usize` and `dimension.unit: SizeUnit)\nfn print_height\u003c'a, A, HeightIdx, UnitIdx\u003e(obj: \u0026'a A) -\u003e ()\nwhere\n    \u0026'a A: PathTraverser\u003cPath!(dimensions.height), HeightIdx, TargetValue = \u0026'a usize\u003e\n        + PathTraverser\u003cPath!(dimensions.unit), UnitIdx, TargetValue = \u0026'a SizeUnit\u003e,\n{\n    println!(\n        \"Height [{} {:?}]\",\n        path!(dimensions.height).get(obj),\n        path!(dimensions.unit).get(obj)\n    );\n}\n```\n\nSee `examples/paths.rs` to see how this works.\n\n### Coproduct\n\nIf you've ever wanted to have an adhoc union / sum type of types that you do not control, you may want\nto take a look at `Coproduct`. In Rust, thanks to `enum`, you could potentially declare one every time you\nwant a sum type to do this, but there is a light-weight way of doing it through Frunk:\n\n```rust\nuse frunk::prelude::*; // for the fold method\n\n// Declare the types we want in our Coproduct\ntype I32F32Bool = Coprod!(i32, f32, bool);\n\nlet co1 = I32F32Bool::inject(3);\nlet get_from_1a: Option\u003c\u0026i32\u003e = co1.get();\nlet get_from_1b: Option\u003c\u0026bool\u003e = co1.get();\n\nassert_eq!(get_from_1a, Some(\u00263));\n// None because co1 does not contain a bool, it contains an i32\nassert_eq!(get_from_1b, None);\n\n// This will fail at compile time because i8 is not in our Coproduct type\nlet nope_get_from_1b: Option\u003c\u0026i8\u003e = co1.get(); // \u003c-- will fail\n// It's also impossible to inject something into a coproduct that is of the wrong type\n// (not contained in the coproduct type)\nlet nope_co = I32F32Bool::inject(42f64); // \u003c-- will fail\n\n// We can fold our Coproduct into a single value by handling all types in it\nassert_eq!(\n    co1.fold(hlist![|i| format!(\"int {}\", i),\n                    |f| format!(\"float {}\", f),\n                    |b| (if b { \"t\" } else { \"f\" }).to_string()]),\n    \"int 3\".to_string());\n```\n\nFor more information, check out the [docs for Coproduct](https://beachape.com/frunk/frunk/coproduct/index.html)\n\n### Validated\n\n`Validated` is a way of running a bunch of operations that can go wrong (for example,\nfunctions returning `Result\u003cT, E\u003e`) and, in the case of one or more things going wrong,\nhaving all the errors returned to you all at once. In the case that everything went well, you get\nan `HList` of all your results.\n\nMapping (and otherwise working with plain) `Result`s is different because it will\nstop at the first error, which can be annoying in the very common case (outlined\nbest by [the Cats project](http://typelevel.org/cats/datatypes/validated.html)).\n\nTo use `Validated`, first:\n```rust\nuse frunk::prelude::*; // for Result::into_validated\n```\n\nAssuming we have a `Person` struct defined\n```rust\n#[derive(PartialEq, Eq, Debug)]\nstruct Person {\n    age: i32,\n    name: String,\n    street: String,\n}\n```\n\nHere is an example of how it can be used in the case that everything goes smoothly.\n\n```rust\nfn get_name() -\u003e Result\u003cString, Error\u003e { /* elided */ }\nfn get_age() -\u003e Result\u003ci32, Error\u003e { /* elided */ }\nfn get_street() -\u003e Result\u003cString, Error\u003e { /* elided */ }\n\n// Build up a `Validated` by adding in any number of `Result`s\nlet validation = get_name().into_validated() + get_age() + get_street();\n// When needed, turn the `Validated` back into a Result and map as usual\nlet try_person = validation.into_result()\n                           // Destructure our hlist\n                           .map(|hlist_pat!(name, age, street)| {\n                               Person {\n                                   name: name,\n                                   age: age,\n                                   street: street,\n                               }\n                           });\n\nassert_eq!(try_person.unwrap(),\n           Person {\n               name: \"James\".to_owned(),\n               age: 32,\n               street: \"Main\".to_owned(),\n           }));\n}\n```\n\nIf, on the other hand, our `Result`s are faulty:\n```rust\n/// This next pair of functions always return Recover::Err\nfn get_name_faulty() -\u003e Result\u003cString, String\u003e {\n    Result::Err(\"crap name\".to_owned())\n}\n\nfn get_age_faulty() -\u003e Result\u003ci32, String\u003e {\n    Result::Err(\"crap age\".to_owned())\n}\n\nlet validation2 = get_name_faulty().into_validated() + get_age_faulty();\nlet try_person2 = validation2.into_result()\n                             .map(|_| unimplemented!());\n\n// Notice that we have an accumulated list of errors!\nassert_eq!(try_person2.unwrap_err(),\n           vec![\"crap name\".to_owned(), \"crap age\".to_owned()]);\n```\n\n### Semigroup\n\nThings that can be combined.\n\n```rust\nuse frunk::Semigroup;\nuse frunk::semigroup::All;\n\nassert_eq!(Some(1).combine(\u0026Some(2)), Some(3));\n\nassert_eq!(All(3).combine(\u0026All(5)), All(1)); // bit-wise \u0026\u0026\nassert_eq!(All(true).combine(\u0026All(false)), All(false));\n```\n\n### Monoid\n\nThings that can be combined *and* have an empty/id value.\n\n```rust\nuse frunk::monoid::combine_all;\n\nlet t1 = (1, 2.5f32, String::from(\"hi\"), Some(3));\nlet t2 = (1, 2.5f32, String::from(\" world\"), None);\nlet t3 = (1, 2.5f32, String::from(\", goodbye\"), Some(10));\nlet tuples = vec![t1, t2, t3];\n\nlet expected = (3, 7.5f32, String::from(\"hi world, goodbye\"), Some(13));\nassert_eq!(combine_all(\u0026tuples), expected)\n\nlet product_nums = vec![Product(2), Product(3), Product(4)];\nassert_eq!(combine_all(\u0026product_nums), Product(24))\n```\n\n### Features\n\nFrunk comes with support for deriving [serde](https://github.com/serde-rs/serde) serializer/deserializers for its core\ndata structures. This can be enabled by adding the `serde` feature flag.\n\nFor example, if you'd like to use just `frunk_core` with serde\n\n```toml\n[dependencies]\nfrunk_core = { version = \"$version\", features = [\"serde\"] }\n```\n\nOr, if you'd like to use `frunk` with serde, you need to explicitly include `frunk_core` as well\n\n```toml\n[dependencies]\nfrunk = { version = \"$version\", features = [\"serde\"] }\nfrunk_core = { version = \"$version\", features = [\"serde\"] }\n```\n\n### Benchmarks\n\nBenchmarks are available in `./benches` and can be run with:\n\n`$ rustup run nightly cargo bench`\n\nBenchmarks on `master` are also [auto-generated, uploaded and available online](https://beachape.com/frunk/dev/bench).\n\n## Todo\n\n### Stabilise interface, general cleanup\n\nBefore a 1.0 release, would be best to revisit the design of the interfaces\nand do some general code (and test cleanup).\n\n### Not yet implemented\n\nGiven that Rust has no support for Higher Kinded Types, I'm not sure if these\nare even possible to implement. In addition, Rustaceans are used to calling `iter()`\non collections to get a lazy view, manipulating their elements with `map`\nor `and_then`, and then doing a `collect()` at the end to keep things\nefficient. The usefulness of these following structures maybe limited in that context.\n\n0. `Functor`\n1. `Monad`\n2. `Apply`\n3. `Applicative`\n\n## Contributing\n\nYes please !\n\nThe following are considered important, in keeping with the spirit of Rust and functional programming:\n\n- Safety (type and memory)\n- Efficiency\n- Correctness\n\n## Inspirations\n\nScalaz, Shapeless, Cats, Haskell, the usual suspects ;)\n\n## Maintainers\n\nA.k.a. people whom you can bug/tag/@ on Gitter :D\n\n1. [lloydmeta](https://github.com/lloydmeta)\n2. [Centril](https://github.com/centril)\n3. [ExpHP](https://github.com/ExpHP)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flloydmeta%2Ffrunk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flloydmeta%2Ffrunk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flloydmeta%2Ffrunk/lists"}