├── .gitignore ├── tests ├── elm-verify-examples.json └── Tests.elm ├── elm-tooling.json ├── elm.json ├── .github └── workflows │ └── test.yml ├── package.json ├── README.md ├── LICENSE └── src └── Maybe └── Extra.elm /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build or dist files 2 | /elm-stuff 3 | /node_modules 4 | /tests/VerifyExamples 5 | -------------------------------------------------------------------------------- /tests/elm-verify-examples.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "../src", 3 | "tests": [ 4 | "Maybe.Extra" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /elm-tooling.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": { 3 | "elm": "0.19.1", 4 | "elm-format": "0.8.5", 5 | "elm-test-rs": "1.2.2" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "elm-community/maybe-extra", 4 | "summary": "Convenience functions for working with Maybe", 5 | "license": "MIT", 6 | "version": "5.3.0", 7 | "exposed-modules": [ 8 | "Maybe.Extra" 9 | ], 10 | "elm-version": "0.19.0 <= v < 0.20.0", 11 | "dependencies": { 12 | "elm/core": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "test-dependencies": { 15 | "elm-explorations/test": "1.2.2 <= v < 2.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Tests" 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-node@v2 11 | with: 12 | node-version: '14' 13 | cache: 'npm' 14 | - uses: actions/cache@v2 15 | with: 16 | path: ~/.elm 17 | key: ${{ runner.os }}-elm-${{ hashFiles('package-lock.json', 'elm-tooling.json', 'elm.json') }} 18 | - run: npm ci 19 | - run: npm test 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maybe-extra", 3 | "scripts": { 4 | "format": "elm-format --yes src tests/Tests.elm", 5 | "test:docs": "elm make --docs=docs.json && rm docs.json", 6 | "test:format": "elm-format --validate src tests/Tests.elm", 7 | "test": "elm-verify-examples && elm-test && npm run test:format && npm run test:docs", 8 | "postinstall": "elm-tooling install" 9 | }, 10 | "devDependencies": { 11 | "elm-doc-preview": "^5.0.5", 12 | "elm-tooling": "^1.6.0", 13 | "elm-verify-examples": "^5.2.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Maybe.Extra 2 | 3 | [![Build Status](https://github.com/elm-community/maybe-extra/actions/workflows/test.yml/badge.svg)](https://github.com/elm-community/maybe-extra/actions/workflows/test.yml) 4 | 5 | Convenience functions for working with Maybe 6 | 7 | ## Contributing 8 | 9 | Pull requests and feature requests are welcome. 10 | If you want to talk to us, join us on the 11 | `#elm-community` channel on the [Elm Slack](https://elmlang.slack.com). 12 | 13 | ## Tests 14 | 15 | This package uses [elm-test](https://github.com/elm-explorations/test) and [elm-verify-examples](https://github.com/stoeffel/elm-verify-examples). 16 | 17 | Run the tests with 18 | - `npm install` 19 | - `npm test` 20 | 21 | Format with [elm-format](https://github.com/avh4/elm-format) 22 | - `npx elm-format` 23 | 24 | Preview the docs with [elm-doc-preview](https://github.com/dmy/elm-doc-preview) 25 | - `npx elm-doc-preview` 26 | 27 | ## License 28 | 29 | The source code for this package is released under the terms of the MIT 30 | license. See the `LICENSE` file. 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 CircuitHub Inc., Elm Community members 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/Tests.elm: -------------------------------------------------------------------------------- 1 | module Tests exposing (suite) 2 | 3 | import Array 4 | import Expect 5 | import Maybe.Extra exposing (..) 6 | import Test exposing (..) 7 | 8 | 9 | suite : Test 10 | suite = 11 | describe "Maybe.Extra" 12 | [ describe "orElse" 13 | [ test "both Just" <| 14 | \() -> 15 | Just 4 16 | |> orElse (Just 5) 17 | |> Expect.equal (Just 4) 18 | , test "pipe input Nothing" <| 19 | \() -> 20 | Nothing 21 | |> orElse (Just 5) 22 | |> Expect.equal (Just 5) 23 | , test "pipe function Nothing" <| 24 | \() -> 25 | Just 4 26 | |> orElse Nothing 27 | |> Expect.equal (Just 4) 28 | , test "both Nothing" <| 29 | \() -> 30 | Nothing 31 | |> orElse Nothing 32 | |> Expect.equal Nothing 33 | ] 34 | , describe "orLazy" 35 | [ test "both Just" <| 36 | \() -> 37 | orLazy (Just 4) (\() -> Just 5) 38 | |> Expect.equal (Just 4) 39 | , test "first Nothing" <| 40 | \() -> 41 | orLazy Nothing (\() -> Just 5) 42 | |> Expect.equal (Just 5) 43 | , test "second Nothing" <| 44 | \() -> 45 | orLazy (Just 4) (\() -> Nothing) 46 | |> Expect.equal (Just 4) 47 | , test "both Nothing" <| 48 | \() -> 49 | orLazy Nothing (\() -> Nothing) 50 | |> Expect.equal Nothing 51 | ] 52 | , describe "orElseLazy" 53 | [ test "both Just" <| 54 | \() -> 55 | Just 4 56 | |> orElseLazy (\() -> Just 5) 57 | |> Expect.equal (Just 4) 58 | , test "pipe input Nothing" <| 59 | \() -> 60 | Nothing 61 | |> orElseLazy (\() -> Just 5) 62 | |> Expect.equal (Just 5) 63 | , test "pipe function Nothing" <| 64 | \() -> 65 | Just 4 66 | |> orElseLazy (\() -> Nothing) 67 | |> Expect.equal (Just 4) 68 | , test "both Nothing" <| 69 | \() -> 70 | Nothing 71 | |> orElseLazy (\() -> Nothing) 72 | |> Expect.equal Nothing 73 | ] 74 | , describe "orListLazy" 75 | [ test "empty" <| 76 | \() -> 77 | [] 78 | |> orListLazy 79 | |> Expect.equal Nothing 80 | , test "all nothing" <| 81 | \() -> 82 | [ \() -> Nothing 83 | , \() -> List.head [] 84 | , \() -> String.toInt "" 85 | ] 86 | |> orListLazy 87 | |> Expect.equal Nothing 88 | ] 89 | , describe "traverseArray" 90 | [ test "empty" <| 91 | \() -> 92 | Array.empty 93 | |> traverseArray (\x -> Just (x * 10)) 94 | |> Expect.equal (Just Array.empty) 95 | , test "all Just" <| 96 | \() -> 97 | [ 1, 2, 3, 4, 5 ] 98 | |> Array.fromList 99 | |> traverseArray (\x -> Just (x * 10)) 100 | |> Expect.equal (Just (Array.fromList [ 10, 20, 30, 40, 50 ])) 101 | , test "one Nothing fails the whole function" <| 102 | \() -> 103 | [ [ 1 ], [ 2, 3 ], [] ] 104 | |> Array.fromList 105 | |> traverseArray List.head 106 | |> Expect.equal Nothing 107 | ] 108 | , describe "combineArray" 109 | [ test "empty" <| 110 | \() -> 111 | Array.empty 112 | |> combineArray 113 | |> Expect.equal (Just Array.empty) 114 | , test "succeed" <| 115 | \() -> 116 | [ Just 1, Just 2, Just 3 ] 117 | |> Array.fromList 118 | |> combineArray 119 | |> Expect.equal (Just (Array.fromList [ 1, 2, 3 ])) 120 | , test "fail" <| 121 | \() -> 122 | [ Just 1, Nothing ] 123 | |> Array.fromList 124 | |> combineArray 125 | |> Expect.equal Nothing 126 | ] 127 | , describe "oneOf" 128 | [ test "empty" <| 129 | \() -> 130 | oneOf [] 0 131 | |> Expect.equal Nothing 132 | , test "all fail" <| 133 | \() -> 134 | oneOf (List.repeat 10 (always Nothing)) 0 135 | |> Expect.equal Nothing 136 | , test "last function succeeds" <| 137 | \() -> 138 | oneOf [ always Nothing, always Nothing, always Nothing, always (Just True) ] 0 139 | |> Expect.equal (Just True) 140 | , test "first function succeeds" <| 141 | \() -> 142 | 0 143 | |> oneOf [ Just, Just << (+) 10, Just << (+) 20 ] 144 | |> Expect.equal (Just 0) 145 | ] 146 | , describe "andThen3" 147 | [ test "returns a Just if it can" <| 148 | \() -> 149 | andThen3 150 | (\a b c -> Just (a + b + c)) 151 | (Just 4) 152 | (Just 2) 153 | (Just 1) 154 | |> Expect.equal (Just 7) 155 | ] 156 | , describe "andThen4" 157 | [ test "returns a Just if it can" <| 158 | \() -> 159 | andThen4 160 | (\a b c d -> Just (a + b + c + d)) 161 | (Just 8) 162 | (Just 4) 163 | (Just 2) 164 | (Just 1) 165 | |> Expect.equal (Just 15) 166 | ] 167 | , describe "unwrap" 168 | [ test "returns the default value if it is a Nothing" <| 169 | \() -> 170 | unwrap 0 String.length Nothing 171 | |> Expect.equal 0 172 | , test "returns the unwrapped value if it is a Just" <| 173 | \() -> 174 | unwrap 0 String.length (Just "abc") 175 | |> Expect.equal 3 176 | ] 177 | , describe "unpack" 178 | [ test "returns the default value if it is a Nothing" <| 179 | \() -> 180 | unpack (\() -> 0) String.length Nothing 181 | |> Expect.equal 0 182 | , test "returns the unpackped value if it is a Just" <| 183 | \() -> 184 | unpack (\() -> 0) String.length (Just "abc") 185 | |> Expect.equal 3 186 | ] 187 | ] 188 | -------------------------------------------------------------------------------- /src/Maybe/Extra.elm: -------------------------------------------------------------------------------- 1 | module Maybe.Extra exposing 2 | ( isJust, isNothing, join, filter 3 | , withDefaultLazy, unwrap, unpack 4 | , or, orElse, orList, orLazy, orElseLazy, orListLazy, oneOf 5 | , values 6 | , combine, traverse, combineArray, traverseArray 7 | , toList, toArray 8 | , cons 9 | , andThen2, andThen3, andThen4 10 | , andMap, next, prev 11 | ) 12 | 13 | {-| Convenience functions for [`Maybe`](https://package.elm-lang.org/packages/elm/core/latest/Maybe). 14 | 15 | 16 | # Basics 17 | 18 | Work with 1 `Maybe` 19 | 20 | @docs isJust, isNothing, join, filter 21 | 22 | 23 | # Get a value out of a `Maybe` 24 | 25 | @docs withDefaultLazy, unwrap, unpack 26 | 27 | 28 | # Or 29 | 30 | Take the first value that's present 31 | 32 | @docs or, orElse, orList, orLazy, orElseLazy, orListLazy, oneOf 33 | 34 | 35 | # Lists of `Maybe`s 36 | 37 | @docs values 38 | @docs combine, traverse, combineArray, traverseArray 39 | 40 | 41 | # toList 42 | 43 | @docs toList, toArray 44 | @docs cons 45 | 46 | 47 | # andThenN 48 | 49 | These functions are just like [`andThen`](https://package.elm-lang.org/packages/elm/core/latest/Maybe#andThen), except they take multiple arguments. 50 | 51 | All arguments must be `Just` and the function must return a `Just` for the result to be `Just`. 52 | 53 | If you need a version of `andThenN` that takes more than 4 arguments, you can chain together [`andMap`](#andMap) calls in a pipeline. 54 | 55 | @docs andThen2, andThen3, andThen4 56 | 57 | 58 | # Applicative Functions 59 | 60 | @docs andMap, next, prev 61 | 62 | -} 63 | 64 | import Array 65 | import Maybe exposing (..) 66 | 67 | 68 | 69 | -- Basics: Work with 1 `Maybe` 70 | 71 | 72 | {-| 73 | 74 | isJust (Just 42) 75 | --> True 76 | 77 | isJust (Just []) 78 | --> True 79 | 80 | isJust Nothing 81 | --> False 82 | 83 | -} 84 | isJust : Maybe a -> Bool 85 | isJust m = 86 | case m of 87 | Nothing -> 88 | False 89 | 90 | Just _ -> 91 | True 92 | 93 | 94 | {-| 95 | 96 | isNothing (Just 42) 97 | --> False 98 | 99 | isNothing (Just []) 100 | --> False 101 | 102 | isNothing Nothing 103 | --> True 104 | 105 | -} 106 | isNothing : Maybe a -> Bool 107 | isNothing m = 108 | case m of 109 | Nothing -> 110 | True 111 | 112 | Just _ -> 113 | False 114 | 115 | 116 | {-| Flattens nested `Maybe`s 117 | 118 | join (Just (Just 1)) 119 | --> Just 1 120 | 121 | join (Just Nothing) 122 | --> Nothing 123 | 124 | join Nothing 125 | --> Nothing 126 | 127 | -} 128 | join : Maybe (Maybe a) -> Maybe a 129 | join mx = 130 | case mx of 131 | Just x -> 132 | x 133 | 134 | Nothing -> 135 | Nothing 136 | 137 | 138 | {-| Keep the `Maybe` only if the predicate function passes 139 | 140 | filter (\v -> v == 1) (Just 1) 141 | --> Just 1 142 | 143 | filter (\v -> v == 2) (Just 1) 144 | --> Nothing 145 | 146 | filter (\v -> v == 1) Nothing 147 | --> Nothing 148 | 149 | -} 150 | filter : (a -> Bool) -> Maybe a -> Maybe a 151 | filter f m = 152 | case m of 153 | Just a -> 154 | if f a then 155 | m 156 | 157 | else 158 | Nothing 159 | 160 | Nothing -> 161 | Nothing 162 | 163 | 164 | 165 | -- Get a value out of a `Maybe` 166 | 167 | 168 | {-| Lazy version of [Maybe.withDefault](https://package.elm-lang.org/packages/elm/core/latest/Maybe#withDefault). 169 | 170 | It will only calculate the default if needed. 171 | 172 | Examples: 173 | 174 | withDefaultLazy (\() -> 2 + 2) Nothing 175 | --> 4 176 | 177 | withDefaultLazy (\() -> Debug.todo "Expensive calculation") (Just 4) 178 | --> 4 179 | 180 | -} 181 | withDefaultLazy : (() -> a) -> Maybe a -> a 182 | withDefaultLazy default m = 183 | case m of 184 | Nothing -> 185 | default () 186 | 187 | Just a -> 188 | a 189 | 190 | 191 | {-| Like using a `case`. 192 | Give a function that says what to do if the input is `Just`, 193 | and a value to use if the input is `Nothing`. 194 | 195 | These are all equivalent: 196 | 197 | unwrap default f maybeX 198 | 199 | maybeX 200 | |> Maybe.map f 201 | |> Maybe.withDefault default 202 | 203 | case maybeX of 204 | Just x -> 205 | f x 206 | 207 | Nothing -> 208 | default 209 | 210 | Except that unlike a `case`, the default value for `unwrap` is always computed. 211 | If your default value is expensive to compute, use the lazy [`unpack`](#unpack) instead. 212 | 213 | Examples: 214 | 215 | unwrap 0 String.length Nothing 216 | --> 0 217 | 218 | unwrap 0 String.length (Just "abc") 219 | --> 3 220 | 221 | -} 222 | unwrap : b -> (a -> b) -> Maybe a -> b 223 | unwrap default f m = 224 | case m of 225 | Nothing -> 226 | default 227 | 228 | Just a -> 229 | f a 230 | 231 | 232 | {-| Like [`unwrap`](#unwrap), but the default value is lazy, 233 | and will only be computed if the `Maybe` is `Nothing`. 234 | 235 | unpack (\() -> 0) String.length Nothing 236 | --> 0 237 | 238 | unpack (\() -> 0) String.length (Just "abc") 239 | --> 3 240 | 241 | `unpack (\() -> default) f maybeX` is equivalent to 242 | 243 | case maybeX of 244 | Just x -> 245 | f x 246 | 247 | Nothing -> 248 | default 249 | 250 | -} 251 | unpack : (() -> b) -> (a -> b) -> Maybe a -> b 252 | unpack default f m = 253 | case m of 254 | Nothing -> 255 | default () 256 | 257 | Just a -> 258 | f a 259 | 260 | 261 | 262 | -- Or: Combine 2 `Maybe`s 263 | 264 | 265 | {-| Returns the first value that is present, like the boolean `||`. 266 | 267 | Both values will be computed. There is no short-circuiting. 268 | If your second argument is expensive to calculate and you need short circuiting, use [`orLazy`](#orLazy) instead. 269 | 270 | or (Just 4) (Just 5) 271 | --> Just 4 272 | 273 | or (Just 4) Nothing 274 | --> Just 4 275 | 276 | or Nothing (Just 5) 277 | --> Just 5 278 | 279 | or Nothing Nothing 280 | --> Nothing 281 | 282 | Advanced functional programmers will recognize this as the 283 | implementation of `mplus` for `Maybe`s from the `MonadPlus` type 284 | class. 285 | 286 | -} 287 | or : Maybe a -> Maybe a -> Maybe a 288 | or ma mb = 289 | case ma of 290 | Nothing -> 291 | mb 292 | 293 | Just _ -> 294 | ma 295 | 296 | 297 | {-| Piping-friendly version of [`or`](#or). 298 | 299 | Just 5 300 | |> orElse (Just 4) 301 | --> Just 5 302 | 303 | orElse (Just 4) (Just 5) 304 | --> Just 5 305 | 306 | List.head [] 307 | |> orElse (List.head [ 4 ]) 308 | --> Just 4 309 | 310 | -} 311 | orElse : Maybe a -> Maybe a -> Maybe a 312 | orElse ma mb = 313 | case mb of 314 | Nothing -> 315 | ma 316 | 317 | Just _ -> 318 | mb 319 | 320 | 321 | {-| Returns the first value that is present. 322 | 323 | All values will be computed. 324 | If your arguments are expensive to calculate, use [`orListLazy`](#orListLazy) instead. 325 | 326 | orList 327 | [ Nothing 328 | , Just 1 329 | , Just 2 330 | ] 331 | --> Just 1 332 | 333 | orList 334 | [ List.head [] 335 | , String.toInt "" 336 | ] 337 | --> Nothing 338 | 339 | orList [] 340 | --> Nothing 341 | 342 | -} 343 | orList : List (Maybe a) -> Maybe a 344 | orList maybes = 345 | case maybes of 346 | [] -> 347 | Nothing 348 | 349 | Nothing :: rest -> 350 | orList rest 351 | 352 | (Just answer) :: _ -> 353 | Just answer 354 | 355 | 356 | {-| Lazy version of [`or`](#or). 357 | 358 | The second argument will only be evaluated if the first argument is `Nothing`. 359 | 360 | orLazy (Just 4) (\() -> Debug.todo "Expensive calculation") 361 | --> Just 4 362 | 363 | -} 364 | orLazy : Maybe a -> (() -> Maybe a) -> Maybe a 365 | orLazy ma fmb = 366 | case ma of 367 | Nothing -> 368 | fmb () 369 | 370 | Just _ -> 371 | ma 372 | 373 | 374 | {-| Lazy version of [`orElse`](#orElse). 375 | Piping-friendly version of [`orLazy`](#orLazy). 376 | 377 | The first argument will only be evaluated if the second argument is `Nothing`. 378 | 379 | Just 4 380 | |> orElseLazy (\() -> Debug.todo "Expensive calculation") 381 | --> Just 4 382 | 383 | -} 384 | orElseLazy : (() -> Maybe a) -> Maybe a -> Maybe a 385 | orElseLazy fma mb = 386 | case mb of 387 | Nothing -> 388 | fma () 389 | 390 | Just _ -> 391 | mb 392 | 393 | 394 | {-| Lazy version of [`orList`](#orList) 395 | 396 | Stops calculating new values after the first match 397 | 398 | orListLazy 399 | [ \() -> Nothing 400 | , \() -> Just 1 401 | , \() -> Debug.todo "Expensive calculation" 402 | ] 403 | --> Just 1 404 | 405 | -} 406 | orListLazy : List (() -> Maybe a) -> Maybe a 407 | orListLazy maybes = 408 | oneOf maybes () 409 | 410 | 411 | {-| Try a list of functions against a value. Return the value of the first call that succeeds (returns `Just`). 412 | 413 | type UserInput 414 | = FloatInput Float 415 | | IntInput Int 416 | | UnknownInput 417 | 418 | "5.6" 419 | |> oneOf 420 | [ String.toInt >> Maybe.map IntInput 421 | , String.toFloat >> Maybe.map FloatInput 422 | ] 423 | |> Maybe.withDefault UnknownInput 424 | --> FloatInput 5.6 425 | 426 | -} 427 | oneOf : List (a -> Maybe b) -> a -> Maybe b 428 | oneOf fmbs a = 429 | case fmbs of 430 | [] -> 431 | Nothing 432 | 433 | fmb :: rest -> 434 | case fmb a of 435 | Just b -> 436 | Just b 437 | 438 | Nothing -> 439 | oneOf rest a 440 | 441 | 442 | 443 | -- Lists of `Maybe`s 444 | 445 | 446 | {-| Take all the values that are present, throwing away any `Nothing`s. 447 | 448 | Equivalent to [`List.filterMap identity`](https://package.elm-lang.org/packages/elm/core/latest/List#filterMap). 449 | 450 | values [ Just 1, Nothing, Just 2 ] 451 | --> [ 1, 2 ] 452 | 453 | -} 454 | values : List (Maybe a) -> List a 455 | values = 456 | List.foldr cons [] 457 | 458 | 459 | {-| If every `Maybe` in the list is present, return all of the values unwrapped. 460 | If there are any `Nothing`s, the whole function fails and returns `Nothing`. 461 | 462 | combine [] 463 | --> Just [] 464 | 465 | combine [ Just 1, Just 2, Just 3 ] 466 | --> Just [ 1, 2, 3 ] 467 | 468 | combine [ Just 1, Nothing, Just 3 ] 469 | --> Nothing 470 | 471 | -} 472 | combine : List (Maybe a) -> Maybe (List a) 473 | combine list = 474 | combineHelp list [] 475 | 476 | 477 | combineHelp : List (Maybe a) -> List a -> Maybe (List a) 478 | combineHelp list acc = 479 | case list of 480 | head :: tail -> 481 | case head of 482 | Just a -> 483 | combineHelp tail (a :: acc) 484 | 485 | Nothing -> 486 | Nothing 487 | 488 | [] -> 489 | Just (List.reverse acc) 490 | 491 | 492 | {-| Like [`combine`](#combine), but map a function over each element of the list first. 493 | 494 | If every function call succeeds (returns `Just`), `traverse` will return a list. 495 | If any function call fails (returns `Nothing`), `traverse` will return `Nothing`. 496 | 497 | `combine` is equivalent to `traverse identity`. 498 | 499 | traverse (\x -> Just (x * 10)) [ 1, 2, 3, 4, 5 ] 500 | --> Just [ 10, 20, 30, 40, 50 ] 501 | 502 | traverse List.head [ [1], [2, 3], [] ] 503 | --> Nothing 504 | 505 | -} 506 | traverse : (a -> Maybe b) -> List a -> Maybe (List b) 507 | traverse f list = 508 | traverseHelp f list [] 509 | 510 | 511 | traverseHelp : (a -> Maybe b) -> List a -> List b -> Maybe (List b) 512 | traverseHelp f list acc = 513 | case list of 514 | head :: tail -> 515 | case f head of 516 | Just a -> 517 | traverseHelp f tail (a :: acc) 518 | 519 | Nothing -> 520 | Nothing 521 | 522 | [] -> 523 | Just (List.reverse acc) 524 | 525 | 526 | {-| Like [`combine`](#combine), 527 | but works on [`Array`](https://package.elm-lang.org/packages/elm/core/latest/Array) instead of `List`. 528 | -} 529 | combineArray : Array.Array (Maybe a) -> Maybe (Array.Array a) 530 | combineArray = 531 | Array.foldl (map2 Array.push) (Just Array.empty) 532 | 533 | 534 | {-| Like [`traverse`](#traverse), 535 | but works on [`Array`](https://package.elm-lang.org/packages/elm/core/latest/Array) instead of `List`. 536 | -} 537 | traverseArray : (a -> Maybe b) -> Array.Array a -> Maybe (Array.Array b) 538 | traverseArray f = 539 | Array.foldl (\x -> map2 Array.push (f x)) (Just Array.empty) 540 | 541 | 542 | 543 | -- toList 544 | 545 | 546 | {-| A `Maybe` is a lot like a list that can only be length 0 or 1. 547 | 548 | Returns a singleton list if the value is present, and an empty list it's missing. 549 | 550 | toList Nothing 551 | --> [] 552 | 553 | toList (Just 1) 554 | --> [ 1 ] 555 | 556 | -} 557 | toList : Maybe a -> List a 558 | toList m = 559 | case m of 560 | Nothing -> 561 | [] 562 | 563 | Just x -> 564 | [ x ] 565 | 566 | 567 | {-| Like `toList`, but returns a singleton or empty [`Array`](https://package.elm-lang.org/packages/elm/core/latest/Array). 568 | 569 | import Array 570 | 571 | toArray Nothing 572 | --> Array.fromList [] 573 | 574 | toArray (Just 1) 575 | --> Array.fromList [ 1 ] 576 | 577 | -} 578 | toArray : Maybe a -> Array.Array a 579 | toArray m = 580 | case m of 581 | Nothing -> 582 | Array.empty 583 | 584 | Just x -> 585 | Array.repeat 1 x 586 | 587 | 588 | {-| Add an item to a list only if it's a `Just`. 589 | 590 | cons (Just 1) [ 2, 3 ] 591 | --> [ 1, 2, 3 ] 592 | 593 | cons Nothing [2, 3 ] 594 | --> [ 2, 3 ] 595 | 596 | -} 597 | cons : Maybe a -> List a -> List a 598 | cons item list = 599 | case item of 600 | Just v -> 601 | v :: list 602 | 603 | Nothing -> 604 | list 605 | 606 | 607 | 608 | -- andThenN 609 | 610 | 611 | {-| 612 | 613 | import Array exposing (Array) 614 | 615 | array : Array Int 616 | array = Array.fromList [1,2,3] 617 | 618 | andThen2 Array.get (Just 1) (Just array) 619 | --> Just 2 620 | 621 | andThen2 Array.get Nothing (Just array) 622 | --> Nothing 623 | 624 | andThen2 Array.get (Just 1) Nothing 625 | --> Nothing 626 | 627 | andThen2 Array.get (Just 4) (Just array) 628 | --> Nothing 629 | 630 | -} 631 | andThen2 : (a -> b -> Maybe value) -> Maybe a -> Maybe b -> Maybe value 632 | andThen2 func ma mb = 633 | case ma of 634 | Just a -> 635 | case mb of 636 | Just b -> 637 | func a b 638 | 639 | Nothing -> 640 | Nothing 641 | 642 | Nothing -> 643 | Nothing 644 | 645 | 646 | {-| -} 647 | andThen3 : (a -> b -> c -> Maybe value) -> Maybe a -> Maybe b -> Maybe c -> Maybe value 648 | andThen3 func ma mb mc = 649 | case ma of 650 | Just a -> 651 | case mb of 652 | Just b -> 653 | case mc of 654 | Just c -> 655 | func a b c 656 | 657 | Nothing -> 658 | Nothing 659 | 660 | Nothing -> 661 | Nothing 662 | 663 | Nothing -> 664 | Nothing 665 | 666 | 667 | {-| -} 668 | andThen4 : (a -> b -> c -> d -> Maybe value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe value 669 | andThen4 func ma mb mc md = 670 | case ma of 671 | Just a -> 672 | case mb of 673 | Just b -> 674 | case mc of 675 | Just c -> 676 | case md of 677 | Just d -> 678 | func a b c d 679 | 680 | Nothing -> 681 | Nothing 682 | 683 | Nothing -> 684 | Nothing 685 | 686 | Nothing -> 687 | Nothing 688 | 689 | Nothing -> 690 | Nothing 691 | 692 | 693 | 694 | -- Applicative Functions 695 | 696 | 697 | {-| If both a function and a value are present, apply the function to the value. 698 | If either argument is `Nothing`, return `Nothing`. 699 | 700 | Just ((+) 2) 701 | |> andMap (Just 3) 702 | --> Just 5 703 | 704 | Nothing 705 | |> andMap (Just 3) 706 | --> Nothing 707 | 708 | Just ((+) 2) 709 | |> andMap Nothing 710 | --> Nothing 711 | 712 | This can be used to do [`Maybe.mapN`](https://package.elm-lang.org/packages/elm/core/latest/Maybe#map2) or [`andThenN`](#andThenN) for any number of arguments. 713 | 714 | -- map4 715 | Just (\a b c d -> a + b + c + d ) 716 | |> andMap (Just 1) 717 | |> andMap (Just 2) 718 | |> andMap (Just 4) 719 | |> andMap (Just 8) 720 | --> Just 15 721 | 722 | -- andThen4 723 | Just (\a b c d -> Just (a + b + c + d )) 724 | |> andMap (Just 1) 725 | |> andMap (Just 2) 726 | |> andMap (Just 4) 727 | |> andMap (Just 8) 728 | |> join 729 | --> Just 15 730 | 731 | Advanced functional programmers will recognize this as the implementation of `<*>` for `Maybe`s from the `Applicative` typeclass. 732 | 733 | -} 734 | andMap : Maybe a -> Maybe (a -> b) -> Maybe b 735 | andMap = 736 | Maybe.map2 (|>) 737 | 738 | 739 | {-| Take two `Maybe` values. If the first one equals `Nothing`, return `Nothing`. Otherwise return the second value. 740 | 741 | next (Just 1) (Just 2) 742 | --> Just 2 743 | 744 | next Nothing (Just 2) 745 | --> Nothing 746 | 747 | next (Just 1) Nothing 748 | --> Nothing 749 | 750 | Advanced functional programmers will recognize this as the implementation of `*>` for `Maybe`s from the `Applicative` typeclass. 751 | 752 | -} 753 | next : Maybe a -> Maybe b -> Maybe b 754 | next a b = 755 | case a of 756 | Nothing -> 757 | Nothing 758 | 759 | Just _ -> 760 | b 761 | 762 | 763 | {-| Take two `Maybe` values. If the second one equals `Nothing`, return `Nothing`. Otherwise return the first value. 764 | 765 | prev (Just 1) (Just 2) 766 | --> Just 1 767 | 768 | prev Nothing (Just 2) 769 | --> Nothing 770 | 771 | prev (Just 1) Nothing 772 | --> Nothing 773 | 774 | Advanced functional programmers will recognize this as the implementation of `<*` for `Maybe`s from the `Applicative` typeclass. 775 | 776 | -} 777 | prev : Maybe a -> Maybe b -> Maybe a 778 | prev a b = 779 | case b of 780 | Nothing -> 781 | Nothing 782 | 783 | Just _ -> 784 | a 785 | --------------------------------------------------------------------------------