├── .editorconfig ├── .envrc.example ├── .gitignore ├── LICENSE.txt ├── MAKERS.txt ├── README.rst ├── elm.json ├── flake.lock ├── flake.nix ├── shell.nix ├── src ├── Either.elm └── Either │ ├── Decode.elm │ └── Prism.elm └── tests └── Tests.elm /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 10 | trim_trailing_whitespace = true 11 | 12 | [*.elm] 13 | charset = utf-8 14 | indent_size = 4 15 | 16 | [*.{json,nix}] 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /.envrc.example: -------------------------------------------------------------------------------- 1 | if type nix &>/dev/null; then 2 | # https://github.com/nix-community/nix-direnv 3 | if nix flake metadata &>/dev/null; then 4 | use flake 5 | else 6 | use nix 7 | fi 8 | fi 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Elm 2 | /elm-stuff 3 | /documentation.json 4 | 5 | # Node 6 | node_modules/ 7 | package-lock.json 8 | 9 | # Nix 10 | /result 11 | 12 | # Direnv 13 | .direnv/ 14 | .envrc 15 | 16 | # Logs 17 | /logs 18 | *.log 19 | 20 | # Editors 21 | *.sw[po] 22 | 23 | # NO to Microsoft 24 | *.code-workspace 25 | .github/ 26 | .vscode/ 27 | 28 | # NO to Docker 29 | *Dockerfile* 30 | *docker-compose* 31 | 32 | # NO to invasive configs 33 | .husky/ 34 | .pre-commit-config.{yaml,yml} 35 | 36 | # OS generated files 37 | .DS_Store 38 | .DS_Store? 39 | ._* 40 | .Spotlight-V100 41 | .Trashes 42 | Icon? 43 | ehthumbs.db 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright © [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MAKERS.txt: -------------------------------------------------------------------------------- 1 | toastal (https://toast.al) 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Either 2 | ====== 3 | 4 | A generic structure for a type with two possibilities: a ``Left a`` or a ``Right b``. 5 | 6 | An `Either` is right-biased, so most operations will be applied to the ``Right``\—although many operations are provided for ``Left`` as well. 7 | 8 | This is similar to Result_ type in core_, but is more generic. 9 | If you’re looking for a data type to do error handling, you should use ``Result`` instead. 10 | 11 | .. |Result| replace:: ``Result`` 12 | .. _Result: http://package.elm-lang.org/packages/elm-lang/core/latest/Result 13 | .. |core| replace:: ``core`` 14 | .. _core: http://package.elm-lang.org/packages/elm/core/latest 15 | 16 | 17 | Project & Community Notes 18 | ========================= 19 | 20 | This project is regrettably available on GitHub_. 21 | The Elm community has tied itself to the closed-source, Microsoft-owned code forge of GitHub for package registry and identity. 22 | This does not protect the privacy or freedom of its community members. 23 | 24 | .. _GitHub: https://github.com/toastal/either 25 | 26 | 27 | License 28 | ======= 29 | 30 | This project is licensed under Apache License 2.0 (Apache-2.0_) - ``LICENSE.txt`` file in this project for details. 31 | 32 | .. _Apache-2.0: https://www.apache.org/licenses/LICENSE-2.0 33 | 34 | 35 | Funding 36 | ======= 37 | 38 | If you want to make a small contribution to th maintenance of this & other projects. 39 | 40 | Crowdfunding 41 | • Liberapay: `@toastal `_ 42 | 43 | Cryptocurrency 44 | • Bitcoin: 39nLVxrXPnD772dEqWFwfZZbfTv5BvV89y_ (`BTC verified on Keybase `_) 45 | • Zcash: t1a9pD1D2SDTTd7dbc15KnKsyYXtGcjHuZZ_ (`ZEC verified on Keybase `_) 46 | 47 | .. _39nLVxrXPnD772dEqWFwfZZbfTv5BvV89y: bitcoin://39nLVxrXPnD772dEqWFwfZZbfTv5BvV89y?message=Funding%20toastal%E2%80%99s%20nvim-tree-sitter-unicode-conceal%20development 48 | .. _t1a9pD1D2SDTTd7dbc15KnKsyYXtGcjHuZZ: zcash://t1a9pD1D2SDTTd7dbc15KnKsyYXtGcjHuZZ?message=Funding%20toastal%E2%80%99s%20nvim-tree-sitter-unicode-conceal%20development 49 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "toastal/either", 4 | "summary": "Either for representing a structure with two types", 5 | "license": "Apache-2.0", 6 | "version": "3.6.3", 7 | "exposed-modules": [ 8 | "Either", 9 | "Either.Decode", 10 | "Either.Prism" 11 | ], 12 | "elm-version": "0.19.0 <= v < 0.20.0", 13 | "dependencies": { 14 | "elm/core": "1.0.0 <= v < 2.0.0", 15 | "elm/json": "1.0.0 <= v < 2.0.0" 16 | }, 17 | "test-dependencies": { 18 | "elm-explorations/test": "1.2.2 <= v < 2.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1705496572, 6 | "narHash": "sha256-rPIe9G5EBLXdBdn9ilGc0nq082lzQd0xGGe092R/5QE=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "842d9d80cfd4560648c785f8a4e6f3b096790e19", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "elm-either"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 | }; 7 | 8 | outputs = { self, nixpkgs, ... }@inputs: 9 | let 10 | name = "elm-either"; 11 | 12 | forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.platforms.unix; 13 | 14 | nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); 15 | in 16 | { 17 | devShells = forAllSystems (system: 18 | let pkgs = nixpkgsFor.${system}; in 19 | { 20 | ${name} = pkgs.mkShell { 21 | inherit name; 22 | buildInputs = with pkgs; [ 23 | nixpkgs-fmt 24 | elmPackages.elm 25 | elmPackages.elm-format 26 | ]; 27 | }; 28 | 29 | default = self.devShells.${system}.${name}; 30 | }); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | (import 2 | ( 3 | fetchTarball { 4 | url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz"; 5 | sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; 6 | } 7 | ) 8 | { 9 | src = ./.; 10 | }).shellNix 11 | -------------------------------------------------------------------------------- /src/Either.elm: -------------------------------------------------------------------------------- 1 | {- 2 | Copyright © 2018–2021 toastal (https://toast.al) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -} 16 | 17 | 18 | module Either exposing 19 | ( Either(..) 20 | , map, mapLeft, mapRight, voidRight, voidLeft, mapBoth, mapEach 21 | , singleton, andMap, andMapLeft, andMapRight, map2, map3, map4 22 | , length, foldl, foldr 23 | , andThen, andThenLeft, andThenRight, andThenBoth 24 | , appendLeft, appendRight 25 | , equals 26 | , toList, toListVia, lefts, rights, partition, biList 27 | , toArray, toArrayVia 28 | , toSet, toSetVia 29 | , toDict, toDictVia 30 | , toMaybe, toMaybeVia, leftToMaybe, rightToMaybe, fromMaybe, leftFromMaybe, rightFromMaybe 31 | , toResult, toResultVia, fromResult 32 | , toTask, toTaskVia 33 | , isLeft, isRight, fromLeft, fromRight, withDefault, unpack, unwrap, swap 34 | ) 35 | 36 | {-| A generic structure for a type with two possibilities: a `Left a` or 37 | a `Right b`. 38 | 39 | 40 | # Definition 41 | 42 | @docs Either 43 | 44 | 45 | # Mapping (Functor & Bifunctor) 46 | 47 | @docs map, mapLeft, mapRight, voidRight, voidLeft, mapBoth, mapEach 48 | 49 | 50 | # Applying (Applicative) 51 | 52 | @docs singleton, andMap, andMapLeft, andMapRight, map2, map3, map4 53 | 54 | 55 | # Folding (Foldable) 56 | 57 | @docs length, foldl, foldr 58 | 59 | 60 | # Chaining (Monad) 61 | 62 | @docs andThen, andThenLeft, andThenRight, andThenBoth 63 | 64 | 65 | # Appending (Semigroup) 66 | 67 | @docs appendLeft, appendRight 68 | 69 | 70 | # Equals (Eq) 71 | 72 | @docs equals 73 | 74 | 75 | # List Helpers 76 | 77 | @docs toList, toListVia, lefts, rights, partition, biList 78 | 79 | 80 | # Array Helpers 81 | 82 | @docs toArray, toArrayVia 83 | 84 | 85 | # Set Helpers 86 | 87 | @docs toSet, toSetVia 88 | 89 | 90 | # Dict Helpers 91 | 92 | @docs toDict, toDictVia 93 | 94 | 95 | # Maybe Helpers 96 | 97 | @docs toMaybe, toMaybeVia, leftToMaybe, rightToMaybe, fromMaybe, leftFromMaybe, rightFromMaybe 98 | 99 | 100 | # Result Helpers 101 | 102 | @docs toResult, toResultVia, fromResult 103 | 104 | 105 | # Task Helpers 106 | 107 | @docs toTask, toTaskVia 108 | 109 | 110 | # Rest of the Helpers 111 | 112 | @docs isLeft, isRight, fromLeft, fromRight, withDefault, unpack, unwrap, swap 113 | 114 | -} 115 | 116 | import Array exposing (Array) 117 | import Dict exposing (Dict) 118 | import List 119 | import Set exposing (Set) 120 | import Task exposing (Task) 121 | 122 | 123 | 124 | -- TYPE DEFINITION -- 125 | 126 | 127 | {-| The only implementation 128 | -} 129 | type Either a b 130 | = Left a 131 | | Right b 132 | 133 | 134 | 135 | -- TYPE CLASSES -- 136 | -- FUNCTOR & BIFUNCTOR 137 | 138 | 139 | {-| Apply a function to an `Either`. If the argument is `Right`, it 140 | will be converted. If the argument is an `Left`, the same left value 141 | will propogate through. 142 | 143 | map ((+) 1) (Left "Hello") == Left "Hello" 144 | 145 | map ((+) 1) (Right 2) == Right 3 146 | 147 | -} 148 | map : (a -> b) -> Either x a -> Either x b 149 | map f e = 150 | case e of 151 | Right b -> 152 | Right (f b) 153 | 154 | Left a -> 155 | Left a 156 | 157 | 158 | {-| Apply a function to the `Left` of an `Either`. 159 | 160 | mapLeft ((+) 1) (Left 2) == Left 3 161 | 162 | mapLeft ((+) 1) (Right 2) == Right 2 163 | 164 | -} 165 | mapLeft : (a -> b) -> Either a x -> Either b x 166 | mapLeft f e = 167 | case e of 168 | Left a -> 169 | Left (f a) 170 | 171 | Right b -> 172 | Right b 173 | 174 | 175 | {-| Alias for `map`. 176 | -} 177 | mapRight : (a -> b) -> Either x a -> Either x b 178 | mapRight = 179 | map 180 | 181 | 182 | {-| Ignore the return value of the `Right` using the specified value instead. 183 | Exactly the same as `<$` in other languages. 184 | 185 | voidRight 2 (Right True) == Right 2 186 | 187 | voidRight 2 (Left "banana") == Left "banana" 188 | 189 | -} 190 | voidRight : a -> Either x b -> Either x a 191 | voidRight f = 192 | map (always f) 193 | 194 | 195 | {-| Ignore the return value of the `Left` using the specified value instead. 196 | _NOT_ the same as `$>` in other languages. 197 | 198 | voidLeft "two" (Right True) == Right True 199 | 200 | voidLeft "two" (Left 6) == Left "two" 201 | 202 | -} 203 | voidLeft : a -> Either b x -> Either a x 204 | voidLeft f = 205 | mapLeft (always f) 206 | 207 | 208 | {-| Apply the first argument function to a `Left` and the second 209 | argument function to a `Right` of an `Either`. 210 | 211 | mapBoth (\s -> s ++ "!!") ((+) 1) (Left "Hello") == Left "Hello!!" 212 | 213 | mapBoth (\s -> s ++ "!!") ((+) 1) (Right 2) == Right 3 214 | 215 | -} 216 | mapBoth : (a -> b) -> (c -> d) -> Either a c -> Either b d 217 | mapBoth f g e = 218 | case e of 219 | Left a -> 220 | Left (f a) 221 | 222 | Right b -> 223 | Right (g b) 224 | 225 | 226 | {-| Not crazy on the name, but apply a function to either the `Left` 227 | or the `Right` where the `Left` and the `Right` are of the same type. 228 | 229 | mapEach ((+) 1) (Left 2) == Left 3 230 | 231 | mapEach ((+) 1) (Right 3) == Right 4 232 | 233 | -} 234 | mapEach : (a -> b) -> Either a a -> Either b b 235 | mapEach f e = 236 | case e of 237 | Right b -> 238 | Right (f b) 239 | 240 | Left a -> 241 | Left (f a) 242 | 243 | 244 | 245 | -- FOLDABLE 246 | 247 | 248 | {-| Returns the length of an `Either`. This happens to be `0` for a 249 | `Left` and `1` for a `Right`. 250 | 251 | length (Left 2) == 0 252 | 253 | length (Right "Sharks") == 1 254 | 255 | -} 256 | length : Either a b -> Int 257 | length e = 258 | case e of 259 | Left _ -> 260 | 0 261 | 262 | _ -> 263 | 1 264 | 265 | 266 | 267 | -- foldl and foldr requires that the return be a Monoid 268 | -- which I can't do without typeclasses 269 | 270 | 271 | {-| Folds an `Either` over a function with an accumulator. If 272 | it is a `Left` the function is applied with the accumulator. 273 | If it is a `Right` only the accumulator is returned. 274 | 275 | foldl (*) 2 (Left 3) == 6 276 | 277 | foldl (*) 2 (Right 3) == 2 278 | 279 | -} 280 | foldl : (a -> b -> b) -> b -> Either a a -> b 281 | foldl f acc e = 282 | case e of 283 | Right _ -> 284 | acc 285 | 286 | Left x -> 287 | f x acc 288 | 289 | 290 | {-| Folds an `Either` over a function with an accumulator. If 291 | it is a `Right` the function is applied with the accumulator. 292 | If it is a `Left` only the accumulator is returned. 293 | 294 | foldr (*) 2 (Left 3) == 2 295 | 296 | foldr (*) 2 (Right 3) == 6 297 | 298 | -} 299 | foldr : (a -> b -> b) -> b -> Either a a -> b 300 | foldr f acc e = 301 | case e of 302 | Left _ -> 303 | acc 304 | 305 | Right x -> 306 | f x acc 307 | 308 | 309 | 310 | -- APPLICATIVE 311 | 312 | 313 | {-| Create a `singleton` from a value to an `Either` with a `Right` 314 | of the same type. Also known as `pure`. Use the `Left` constructor 315 | for a singleton of the `Left` variety. 316 | 317 | singleton 2 == Right 2 318 | 319 | -} 320 | singleton : b -> Either x b 321 | singleton = 322 | Right 323 | 324 | 325 | {-| Apply the function that is inside `Either` to a value that is inside 326 | `Either`. Return the result inside `Either`. If one of the `Either` 327 | arguments is `Left x`, return `Left x`. Also known as `apply`. 328 | 329 | Left "Hello" |> andMap (Left "World") == Left "Hello" 330 | 331 | Left "Hello" |> andMap (Right 2) == Left "Hello" 332 | 333 | Right ((+) 1) |> andMap (Left "World") == Left "World" 334 | 335 | Right ((+) 1) |> andMap (Right 2) == Right 3 336 | 337 | -} 338 | andMap : Either x a -> Either x (a -> b) -> Either x b 339 | andMap e e1 = 340 | case e1 of 341 | Right f -> 342 | map f e 343 | 344 | Left x -> 345 | Left x 346 | 347 | 348 | {-| Apply the function that is inside `Either` to a value that is inside 349 | `Either`. Return the result inside `Either`. If one of the `Either` 350 | arguments is `Right x`, return `Right x`. Also known as `apply`. 351 | 352 | Left (\s -> s ++ "!!") |> andMap (Left "Hello") == Left "Hello!!" 353 | 354 | Left (\s -> s ++ "!!") |> andMap (Right 2) == Right 2 355 | 356 | Right 99 |> andMap (Left "World") == Right 99 357 | 358 | Right 99 |> andMap (Right 2) == Right 99 359 | 360 | -} 361 | andMapLeft : Either a x -> Either (a -> b) x -> Either b x 362 | andMapLeft e e1 = 363 | case e1 of 364 | Left f -> 365 | mapLeft f e 366 | 367 | Right x -> 368 | Right x 369 | 370 | 371 | {-| Alias for `andMap`. 372 | -} 373 | andMapRight : Either x a -> Either x (a -> b) -> Either x b 374 | andMapRight = 375 | andMap 376 | 377 | 378 | {-| Apply a function to two eithers, if both arguments are `Right`. 379 | If not, the first argument which is a `Left` will propagate through. 380 | Also known as `liftA2`. 381 | 382 | map2 (+) (Left "Hello") (Left "World") == Left "Hello" 383 | 384 | map2 (+) (Left "Hello") (Right 3) == Left "Hello" 385 | 386 | map2 (+) (Right 2) (Left "World") == Left "World" 387 | 388 | map2 (+) (Right 2) (Right 3) == Right 5 389 | 390 | It’s essentially a helper for (and why it’s under applicative) 391 | 392 | singleton (+) |> andMap (Right 2) |> andMap (Right 3) == Right 5 393 | 394 | -} 395 | map2 : (a -> b -> c) -> Either x a -> Either x b -> Either x c 396 | map2 f e e1 = 397 | map f e |> andMap e1 398 | 399 | 400 | {-| Like `map2`, but with 3 eithers. Also known as `liftA3` 401 | -} 402 | map3 : (a -> b -> c -> d) -> Either x a -> Either x b -> Either x c -> Either x d 403 | map3 f e e1 e2 = 404 | map f e |> andMap e1 |> andMap e2 405 | 406 | 407 | {-| Like `map2`, but with 4 eithers. Also known as `liftA4` 408 | -} 409 | map4 : (a -> b -> c -> d -> e) -> Either x a -> Either x b -> Either x c -> Either x d -> Either x e 410 | map4 f e e1 e2 e3 = 411 | map f e |> andMap e1 |> andMap e2 |> andMap e3 412 | 413 | 414 | 415 | -- MONAD 416 | 417 | 418 | {-| Chain together in many computations that will stop computing if 419 | a chain is on a `Left`. Also known as `bind`. 420 | 421 | Left "Hello" |> andThen (\i -> Right (i + 1)) == Left "Hello" 422 | 423 | Right 2 |> andThen (\i -> Right (i + 1)) == Right 3 424 | 425 | -} 426 | andThen : (a -> Either x b) -> Either x a -> Either x b 427 | andThen f e = 428 | case e of 429 | Right b -> 430 | f b 431 | 432 | Left a -> 433 | Left a 434 | 435 | 436 | {-| Chain together in many computations that will stop computing if 437 | a chain is on a `Right`. Also known as `bind`. 438 | 439 | Left "Hello" |> andThen (\s -> Left (s ++ "!!")) == Left "Hello!!" 440 | 441 | Right 2 |> andThen (\s -> Left (s ++ "!!")) == Right 2 442 | 443 | -} 444 | andThenLeft : (a -> Either b x) -> Either a x -> Either b x 445 | andThenLeft f e = 446 | case e of 447 | Left a -> 448 | f a 449 | 450 | Right b -> 451 | Right b 452 | 453 | 454 | {-| Alias for `andThen`. 455 | -} 456 | andThenRight : (a -> Either x b) -> Either x a -> Either x b 457 | andThenRight = 458 | andThen 459 | 460 | 461 | {-| -} 462 | andThenBoth : (a -> Either c d) -> (b -> Either c d) -> Either a b -> Either c d 463 | andThenBoth f g e = 464 | case e of 465 | Right b -> 466 | g b 467 | 468 | Left a -> 469 | f a 470 | 471 | 472 | 473 | -- SEMIGROUP 474 | 475 | 476 | {-| Append inner values of a `Left`. 477 | 478 | appendLeft (Left [ 1, 2 ]) (Left [ 3, 4 ]) == Left [ 1, 2, 3, 4 ] 479 | 480 | appendLeft (Left [ 1, 2 ]) (Right 'b') == Left [ 1, 2 ] 481 | 482 | appendLeft (Right 'a') (Left [ 3, 4 ]) == Left [ 3, 4 ] 483 | 484 | appendLeft (Right 'a') (Right 'b') == Right 'a' 485 | 486 | -} 487 | appendLeft : Either appendable x -> Either appendable x -> Either appendable x 488 | appendLeft e e1 = 489 | mapLeft (++) e |> andMapLeft e1 490 | 491 | 492 | {-| Append inner values of a `Right`. 493 | 494 | appendRight (Right "Hello") (Right "World") == Right "HelloWorld" 495 | 496 | appendRight (Right "Hello") (Left 1) == Right "Hello" 497 | 498 | appendRight (Left 0) (Right "World") == Right "World" 499 | 500 | appendRight (Left 0) (Left 1) == Left 0 501 | 502 | -} 503 | appendRight : Either x appendable -> Either x appendable -> Either x appendable 504 | appendRight e e1 = 505 | map (++) e |> andMap e1 506 | 507 | 508 | 509 | -- EQ 510 | 511 | 512 | {-| One rendition of equals assuming both sides of the `Either` are comparable. 513 | 514 | equals (Right "Hello") (Right "Hello") == True 515 | 516 | equals (Right "Hello") (Right "World") == False 517 | 518 | equals (Right "Hello") (Left "Hello") == False 519 | 520 | equals (Right "Hello") (Left 1) == False 521 | 522 | equals (Left 0) (Right "World") == False 523 | 524 | equals (Left 0) (Left 0) == True 525 | 526 | equals (Left 0) (Left 1) == False 527 | 528 | equals (Left 0) (Right 0) == False 529 | 530 | -} 531 | equals : Either comparable comparable1 -> Either comparable comparable1 -> Bool 532 | equals e e1 = 533 | case ( e, e1 ) of 534 | ( Right a, Right b ) -> 535 | a == b 536 | 537 | ( Left a, Left b ) -> 538 | a == b 539 | 540 | _ -> 541 | False 542 | 543 | 544 | 545 | -- LIST HELPERS 546 | 547 | 548 | {-| Converts a `Either x b` to a `List` of `b`. 549 | 550 | toList (Right 1) == [ 1 ] 551 | 552 | toList (Left 'a') == [] 553 | 554 | -} 555 | toList : Either x a -> List a 556 | toList = 557 | toListVia identity 558 | 559 | 560 | {-| Folds a `Either x b` to a `List` of `c` via a transforming function. 561 | 562 | toList ((+) 1) (Right 1) == [ 2 ] 563 | 564 | toList ((+) 1) (Left 'a') == [] 565 | 566 | -} 567 | toListVia : (a -> b) -> Either x a -> List b 568 | toListVia f = 569 | unwrap [] (f >> List.singleton) 570 | 571 | 572 | {-| Converts a `List` of `Either a x` to a List of `a`. 573 | 574 | lefts [ Left "Hello", Left "world", Right 2 ] == [ "Hello", "world" ] 575 | 576 | -} 577 | lefts : List (Either a x) -> List a 578 | lefts = 579 | List.foldr (\e acc -> unwrapLeft acc (\a -> a :: acc) e) [] 580 | 581 | 582 | {-| Converts a `List` of `Either x b` to a List of `b`. 583 | 584 | rights [ Left "Hello", Left, "world", Right 2 ] == [ 2 ] 585 | 586 | -} 587 | rights : List (Either x a) -> List a 588 | rights = 589 | List.foldr (\e acc -> unwrap acc (\a -> a :: acc) e) [] 590 | 591 | 592 | {-| Converts a `List` of `Either a b`, into a tuple2 where 593 | the first value is a `List a` and the second is `List b`. 594 | 595 | partition [ Left "Hello", Left "world", Right 2 ] == ( [ "Hello", "World" ], [ 2 ] ) 596 | 597 | -} 598 | partition : List (Either a b) -> ( List a, List b ) 599 | partition = 600 | let 601 | fn e ( ls, rs ) = 602 | case e of 603 | Right b -> 604 | ( ls, b :: rs ) 605 | 606 | Left a -> 607 | ( a :: ls, rs ) 608 | in 609 | List.foldr fn ( [], [] ) 610 | 611 | 612 | {-| Collects the list of elements of a structure, from left to right. 613 | 614 | biList (Left 4) == [ 4 ] 615 | 616 | biList (Right 9) == [ 9 ] 617 | 618 | -} 619 | biList : Either a a -> List a 620 | biList = 621 | unpack List.singleton List.singleton 622 | 623 | 624 | 625 | -- ARRAY HELPERS 626 | 627 | 628 | {-| Convert from `Either` to `Array` 629 | 630 | toArray (Right 1) == Array.fromList [ 1 ] 631 | 632 | toArray (Left 'a') == Array.fromList [] 633 | 634 | -} 635 | toArray : Either x b -> Array b 636 | toArray = 637 | toArrayVia identity 638 | 639 | 640 | {-| Folds from `Either` to `Array` via a transforming function. 641 | 642 | toArrayVia ((+) 1) (Right 1) == Array.fromList [ 2 ] 643 | 644 | toArrayVia ((+) 1) (Left 'a') == Array.fromList [] 645 | 646 | -} 647 | toArrayVia : (a -> b) -> Either x a -> Array b 648 | toArrayVia f = 649 | unwrap Array.empty (\a -> Array.initialize 1 (\_ -> f a)) 650 | 651 | 652 | 653 | -- SET HELPERS 654 | 655 | 656 | {-| Convert from `Either` to `Set` 657 | 658 | toSet (Right 1) == Set.fromList [ 1 ] 659 | 660 | toSet (Left 'a') == Set.fromList [] 661 | 662 | -} 663 | toSet : Either x comparable -> Set comparable 664 | toSet = 665 | toSetVia identity 666 | 667 | 668 | {-| Folds from `Either` to `Set` via a transforming function. 669 | 670 | toSetVia ((+) 1) (Right 1) == Set.fromList [ 2 ] 671 | 672 | toSetVia ((+) 1) (Left 'a') == Set.fromList [] 673 | 674 | -} 675 | toSetVia : (a -> comparable) -> Either x a -> Set comparable 676 | toSetVia f = 677 | unwrap Set.empty (f >> Set.singleton) 678 | 679 | 680 | 681 | -- DICT HELPERS 682 | 683 | 684 | {-| Convert from `Either` to `Dict` 685 | 686 | toDict (Right ( "KEY", 1 )) == Dict.fromList [ ( "KEY", 1 ) ] 687 | 688 | toDict (Left 'a') == Dict.fromList [] 689 | 690 | -} 691 | toDict : Either x ( comparable, v ) -> Dict comparable v 692 | toDict = 693 | toDictVia identity 694 | 695 | 696 | {-| Folds from `Either` to `Dict` via a transforming function. 697 | 698 | toDictVia (Right ( "KEY", 1 )) == Dict.fromList [ ( "KEY", 1 ) ] 699 | 700 | toDictVia (Left 'a') == Dict.fromList [] 701 | 702 | -} 703 | toDictVia : (a -> ( comparable, v )) -> Either x a -> Dict comparable v 704 | toDictVia f = 705 | unwrap Dict.empty (f >> (\( k, v ) -> Dict.singleton k v)) 706 | 707 | 708 | 709 | -- MAYBE HELPERS 710 | 711 | 712 | {-| `Maybe` get the `Right` side of an `Either`. 713 | 714 | toMaybe (Right 2) == Just 2 715 | 716 | toMaybe (Left "World") == Nothing 717 | 718 | -} 719 | toMaybe : Either x b -> Maybe b 720 | toMaybe = 721 | toMaybeVia identity 722 | 723 | 724 | {-| Folds a `Maybe` get the `Right` side of an `Either` via a transforming 725 | function. 726 | 727 | toMaybeVia ((*) 3) (Right 2) == Just 6 728 | 729 | toMaybeVia ((*) 3) (Left "World") == Nothing 730 | 731 | -} 732 | toMaybeVia : (b -> c) -> Either x b -> Maybe c 733 | toMaybeVia f = 734 | unwrap Nothing (f >> Just) 735 | 736 | 737 | {-| `Maybe` get the `Left` side of an `Either`. 738 | 739 | leftToMaybe (Left "World") == Just "World" 740 | 741 | leftToMaybe (Right 2) == Nothing 742 | 743 | -} 744 | leftToMaybe : Either a x -> Maybe a 745 | leftToMaybe e = 746 | case e of 747 | Right _ -> 748 | Nothing 749 | 750 | Left x -> 751 | Just x 752 | 753 | 754 | {-| Alias for `toMaybe`. 755 | -} 756 | rightToMaybe : Either x b -> Maybe b 757 | rightToMaybe = 758 | toMaybe 759 | 760 | 761 | {-| Convert from a `Maybe` to `Either` with a default value 762 | for `Left` for `Nothing`. 763 | 764 | fromMaybe "Hello" (Just 2) == Right 2 765 | 766 | fromMaybe "Hello" Nothing == Left "Hello" 767 | 768 | -} 769 | fromMaybe : a -> Maybe b -> Either a b 770 | fromMaybe d m = 771 | case m of 772 | Nothing -> 773 | Left d 774 | 775 | Just v -> 776 | Right v 777 | 778 | 779 | {-| Convert from a `Maybe` to `Either` with a default value 780 | for `Right` for `Nothing`. 781 | 782 | leftFromMaybe 3 (Just "World") == Left "World" 783 | 784 | leftFromMaybe 3 Nothing == Right 3 785 | 786 | -} 787 | leftFromMaybe : b -> Maybe a -> Either a b 788 | leftFromMaybe d m = 789 | case m of 790 | Nothing -> 791 | Right d 792 | 793 | Just v -> 794 | Left v 795 | 796 | 797 | {-| Alias for `fromMaybe`. 798 | -} 799 | rightFromMaybe : a -> Maybe b -> Either a b 800 | rightFromMaybe = 801 | fromMaybe 802 | 803 | 804 | 805 | -- RESULT HELPERS 806 | 807 | 808 | {-| Convert from `Either` to `Result`. 809 | 810 | toResult (Right 2) == Ok 2 811 | 812 | toResult (Left "World") == Err "World" 813 | 814 | -} 815 | toResult : Either a b -> Result a b 816 | toResult = 817 | toResultVia identity 818 | 819 | 820 | {-| Fold from `Either` to `Result` via transforming function. 821 | 822 | toResultVia ((+) 3) (Left "World") == Err "World" 823 | 824 | toResultVia ((+) 3) (Right 2) == Ok 5 825 | 826 | -} 827 | toResultVia : (b -> c) -> Either a b -> Result a c 828 | toResultVia f = 829 | unpack Err (f >> Ok) 830 | 831 | 832 | {-| Convert from `Result` to `Either`. 833 | 834 | fromResult (Ok 2) == Right 2 835 | 836 | fromResult (Err "World") == Left "World" 837 | 838 | -} 839 | fromResult : Result a b -> Either a b 840 | fromResult r = 841 | case r of 842 | Err a -> 843 | Left a 844 | 845 | Ok b -> 846 | Right b 847 | 848 | 849 | 850 | -- TASK HELPERS 851 | 852 | 853 | {-| Convert from `Either` to `Task` 854 | 855 | toTask (Right "World") -- succeed "World" 856 | 857 | toTask (Left 2) -- fail 2 858 | 859 | -} 860 | toTask : Either a b -> Task a b 861 | toTask = 862 | toTaskVia identity 863 | 864 | 865 | {-| Fold from `Either` to `Task` via transforming function. 866 | 867 | toTaskVia (\s -> s ++ "!!") (Right "World") -- succeed "World!!" 868 | 869 | toTaskVia (\s -> s ++ "!!") (Left 2) -- fail 2 870 | 871 | -} 872 | toTaskVia : (b -> c) -> Either a b -> Task a c 873 | toTaskVia f = 874 | unpack Task.fail (f >> Task.succeed) 875 | 876 | 877 | 878 | -- REST OF THE HELPERS 879 | 880 | 881 | {-| Returns `True` if argument is `Left _` 882 | 883 | isLeft (Left "World") == True 884 | 885 | isLeft (Right 2) == False 886 | 887 | -} 888 | isLeft : Either a b -> Bool 889 | isLeft e = 890 | case e of 891 | Left _ -> 892 | True 893 | 894 | _ -> 895 | False 896 | 897 | 898 | {-| Returns `True` if argument is `Right _` 899 | 900 | isRight (Left "World") == False 901 | 902 | isRight (Right 2) == True 903 | 904 | -} 905 | isRight : Either a b -> Bool 906 | isRight e = 907 | case e of 908 | Right _ -> 909 | True 910 | 911 | _ -> 912 | False 913 | 914 | 915 | {-| Extract left value or a default. 916 | 917 | fromLeft "World" (Left "Hello") == "Hello" 918 | 919 | fromLeft "World" (Right 2) == "World" 920 | 921 | -} 922 | fromLeft : a -> Either a b -> a 923 | fromLeft d e = 924 | case e of 925 | Left a -> 926 | a 927 | 928 | _ -> 929 | d 930 | 931 | 932 | {-| Extract right value or a default. 933 | 934 | fromRight 3 (Left "Hello") == 3 935 | 936 | fromRight 3 (Right 2) == 2 937 | 938 | -} 939 | fromRight : b -> Either a b -> b 940 | fromRight d e = 941 | case e of 942 | Right b -> 943 | b 944 | 945 | _ -> 946 | d 947 | 948 | 949 | {-| Alias for `fromRight`. 950 | -} 951 | withDefault : b -> Either a b -> b 952 | withDefault = 953 | fromRight 954 | 955 | 956 | {-| Given a function for both `Left` and `Right` to to type a generic 957 | type `c`, collapse down the `Either` to a value of that type. 958 | 959 | unpack identity toString (Left "World") == "World" 960 | 961 | unpack identity toString (Right 2) == "2" 962 | 963 | -} 964 | unpack : (a -> c) -> (b -> c) -> Either a b -> c 965 | unpack f g e = 966 | case e of 967 | Right b -> 968 | g b 969 | 970 | Left a -> 971 | f a 972 | 973 | 974 | {-| Apply a function to `Right` value. If argument was a `Left` use the 975 | default value. Equivalent to `Either.map >> Either.fromRight`. 976 | 977 | unwrap 99 ((+) 1) (Left "Hello") == 99 978 | 979 | unwrap 99 ((+) 1) (Right 2) == 3 980 | 981 | -} 982 | unwrap : b -> (a -> b) -> Either x a -> b 983 | unwrap d f e = 984 | case e of 985 | Left _ -> 986 | d 987 | 988 | Right a -> 989 | f a 990 | 991 | 992 | unwrapLeft : b -> (a -> b) -> Either a x -> b 993 | unwrapLeft d f e = 994 | case e of 995 | Right _ -> 996 | d 997 | 998 | Left a -> 999 | f a 1000 | 1001 | 1002 | {-| Swap the `Left` and `Right` sides of an `Either`. 1003 | 1004 | swap (Left "World") == Right "World" 1005 | 1006 | swap (Right 2) == Left 2 1007 | 1008 | -} 1009 | swap : Either a b -> Either b a 1010 | swap = 1011 | unpack Right Left 1012 | -------------------------------------------------------------------------------- /src/Either/Decode.elm: -------------------------------------------------------------------------------- 1 | {- 2 | Copyright © 2018–2021 toastal (https://toast.al) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -} 16 | 17 | 18 | module Either.Decode exposing (either) 19 | 20 | {-| Provides a JSON 21 | [`Decoder`](http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#Decoder) 22 | for the `Either` type. 23 | 24 | 25 | # Definition 26 | 27 | @docs either 28 | 29 | -} 30 | 31 | import Either exposing (Either(..)) 32 | import Json.Decode as Decode exposing (Decoder) 33 | 34 | 35 | {-| Decode an `Either` from a JSON 36 | [`Value`](http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#Value). 37 | 38 | decodeString (either string int) "4" == Ok (Right 4) 39 | 40 | -} 41 | either : Decoder a -> Decoder b -> Decoder (Either a b) 42 | either a b = 43 | Decode.oneOf 44 | [ Decode.map Right b 45 | , Decode.map Left a 46 | ] 47 | -------------------------------------------------------------------------------- /src/Either/Prism.elm: -------------------------------------------------------------------------------- 1 | {- 2 | Copyright © 2018–2021 toastal (https://toast.al) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -} 16 | 17 | 18 | module Either.Prism exposing (Prism, leftp, rightp) 19 | 20 | {-| [elm-monocle](http://package.elm-lang.org/packages/arturopala/elm-monocle/latest)-compatible `Prism`s. 21 | 22 | @docs Prism, leftp, rightp 23 | 24 | -} 25 | 26 | import Either exposing (..) 27 | 28 | 29 | {-| Constructor 30 | -} 31 | type alias Prism a b = 32 | { getOption : a -> Maybe b 33 | , reverseGet : b -> a 34 | } 35 | 36 | 37 | {-| `Prism` for the `Left` value. Also known as `_Left` in other languages. 38 | 39 | .getOption leftp (Left 1) == Just 1 40 | 41 | .getOption leftp (Right "fish") == Nothing 42 | 43 | .reverseGet leftp 2 == Left 2 44 | 45 | -} 46 | leftp : Prism (Either a x) a 47 | leftp = 48 | { getOption = leftToMaybe, reverseGet = Left } 49 | 50 | 51 | {-| `Prism` for the `Right` value. Also known as `_Right` in other languages. 52 | 53 | .getOption rightp (Left 1) == Nothing 54 | 55 | .getOption rightp (Right "fish") == Just "fish" 56 | 57 | .reverseGet rightp "phish" == Right "phish" 58 | 59 | -} 60 | rightp : Prism (Either x b) b 61 | rightp = 62 | { getOption = rightToMaybe, reverseGet = Right } 63 | -------------------------------------------------------------------------------- /tests/Tests.elm: -------------------------------------------------------------------------------- 1 | module Tests exposing (all) 2 | 3 | import Either exposing (..) 4 | import Expect 5 | import Fuzz exposing (int) 6 | import Test exposing (Test, describe, fuzz2, fuzz3, test) 7 | 8 | 9 | all : Test 10 | all = 11 | describe "Testing Either" 12 | [ functor 13 | , applicative 14 | ] 15 | 16 | 17 | functor : Test 18 | functor = 19 | describe "Functor" 20 | [ test "Functor Law I: map identity = identity" <| 21 | \() -> 22 | let 23 | e = 24 | singleton () 25 | in 26 | map identity e 27 | |> Expect.equal (identity e) 28 | , test "Companion to Functor Law I" <| 29 | \() -> 30 | let 31 | e = 32 | Left () 33 | in 34 | mapLeft identity e 35 | |> Expect.equal (identity e) 36 | , fuzz3 37 | int 38 | int 39 | int 40 | "Functor Law II: map (g << f) = map g << map f" 41 | <| 42 | \x y z -> 43 | let 44 | e = 45 | singleton x 46 | 47 | f = 48 | (+) y 49 | 50 | g = 51 | (*) z 52 | in 53 | map (g << f) e 54 | |> Expect.equal (map g << map f <| e) 55 | , fuzz3 56 | int 57 | int 58 | int 59 | "Companion to Functor Law II" 60 | <| 61 | \x y z -> 62 | let 63 | e = 64 | Left x 65 | 66 | f = 67 | (+) y 68 | 69 | g = 70 | (*) z 71 | in 72 | mapLeft (g << f) e 73 | |> Expect.equal (mapLeft g << mapLeft f <| e) 74 | ] 75 | 76 | 77 | applicative : Test 78 | applicative = 79 | describe "Applicative" 80 | [ test "Applicative Identity Law: singleton identity |> andMap v == v" <| 81 | \() -> 82 | let 83 | v = 84 | Right () 85 | in 86 | singleton identity 87 | |> andMap v 88 | |> Expect.equal v 89 | , test "Companion to Applicative Identity Law" <| 90 | \() -> 91 | let 92 | v = 93 | Left () 94 | in 95 | Left identity 96 | |> andMapLeft v 97 | |> Expect.equal v 98 | , fuzz2 99 | int 100 | int 101 | "Applicative Homomorphism Law: singleton f |> andMap singleton x == singleton (f x)" 102 | <| 103 | \x y -> 104 | let 105 | f = 106 | (+) x 107 | in 108 | singleton f 109 | |> andMap (singleton y) 110 | |> Expect.equal (singleton <| f y) 111 | , fuzz2 112 | int 113 | int 114 | "Companion to Applicative Homomorphism Law" 115 | <| 116 | \x y -> 117 | let 118 | f = 119 | (+) x 120 | in 121 | Left f 122 | |> andMapLeft (Left y) 123 | |> Expect.equal (Left <| f y) 124 | , fuzz2 125 | int 126 | int 127 | "Applicative Interchange Law: u |> andMap (singleton y) == singleton ((|>) y) |> andMap u" 128 | <| 129 | \x y -> 130 | let 131 | u = 132 | singleton <| (+) x 133 | in 134 | u 135 | |> andMap (singleton y) 136 | |> Expect.equal ((|>) y |> singleton |> andMap u) 137 | , fuzz2 138 | int 139 | int 140 | "Companion to Applicative Interchange Law" 141 | <| 142 | \x y -> 143 | let 144 | u = 145 | Left <| (+) x 146 | in 147 | u 148 | |> andMapLeft (Left y) 149 | |> Expect.equal ((|>) y |> Left |> andMapLeft u) 150 | ] 151 | --------------------------------------------------------------------------------