├── .gitignore ├── README.md ├── elm-package.json ├── LICENSE └── src └── Lazy └── List.elm /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lazy list implementation in Elm 2 | 3 | Lazy lists are useful for large trees where you will only need one path. They power shrinking in elm-test. 4 | 5 | Prior to 0.18, this repo was elm-lazy-list. Starting with the 0.18 release, the repo is renamed lazy-list and versioning resets to 1.0.0. 6 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "Lazy list implementation in Elm", 4 | "repository": "https://github.com/elm-community/lazy-list.git", 5 | "license": "BSD-3-Clause", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [ 10 | "Lazy.List" 11 | ], 12 | "dependencies": { 13 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 14 | "elm-lang/lazy": "2.0.0 <= v < 3.0.0" 15 | }, 16 | "elm-version": "0.18.0 <= v < 0.19.0" 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Elm Community 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/Lazy/List.elm: -------------------------------------------------------------------------------- 1 | module Lazy.List exposing (..) 2 | 3 | {-| Lazy list implementation in Elm. 4 | 5 | # Types 6 | @docs LazyList, LazyListView 7 | 8 | # Constructors 9 | @docs cons, empty, singleton 10 | 11 | # Query operations 12 | @docs isEmpty, head, tail, headAndTail, member, length 13 | 14 | # Conversions 15 | @docs toList, fromList, toArray, fromArray 16 | 17 | # Map-reduce et al. 18 | @docs map, zip, reduce, flatten, append, foldl, foldr 19 | 20 | # Common operations 21 | @docs intersperse, interleave, reverse, cycle, iterate, repeat, take, takeWhile, drop, dropWhile 22 | 23 | # Filtering operations 24 | @docs keepIf, dropIf, filterMap, unique 25 | 26 | # Chaining operations 27 | @docs andMap, andThen 28 | 29 | # Useful math stuff 30 | @docs numbers, sum, product 31 | 32 | # All the maps! 33 | @docs map2, map3, map4, map5 34 | 35 | # All the zips! 36 | @docs zip3, zip4, zip5 37 | 38 | # All the Cartesian products! 39 | **Warning:** Calling these functions on large lists and then calling `toList` can easily overflow the stack. Consider 40 | passing the results to `take aConstantNumber`. 41 | 42 | @docs product2, product3, product4, product5 43 | 44 | # Infix Operators 45 | @docs (:::), (+++) 46 | 47 | -} 48 | 49 | import Array exposing (Array) 50 | import List 51 | import Random exposing (Generator, Seed) 52 | import Lazy exposing (Lazy, lazy, force) 53 | 54 | 55 | {-| Analogous to `List` type. This is the actual implementation type for the 56 | `LazyList` type. This type is exposed to the user if the user so wishes to 57 | do pattern matching or understand how the list type works. It is not 58 | recommended to work with this type directly. Try working solely with the 59 | provided functions in the package. 60 | -} 61 | type LazyListView a 62 | = Nil 63 | | Cons a (LazyList a) 64 | 65 | 66 | {-| Lazy List type. 67 | -} 68 | type alias LazyList a = 69 | Lazy (LazyListView a) 70 | 71 | 72 | {-| Create an empty list. 73 | -} 74 | empty : LazyList a 75 | empty = 76 | lazy <| 77 | \() -> Nil 78 | 79 | 80 | {-| Create a singleton list. 81 | -} 82 | singleton : a -> LazyList a 83 | singleton a = 84 | cons a empty 85 | 86 | 87 | {-| Detect if a list is empty or not. 88 | -} 89 | isEmpty : LazyList a -> Bool 90 | isEmpty list = 91 | case force list of 92 | Nil -> 93 | True 94 | 95 | _ -> 96 | False 97 | 98 | 99 | {-| Add a value to the front of a list. 100 | -} 101 | cons : a -> LazyList a -> LazyList a 102 | cons a list = 103 | lazy <| 104 | \() -> 105 | Cons a list 106 | 107 | 108 | {-| Get the head of a list. 109 | -} 110 | head : LazyList a -> Maybe a 111 | head list = 112 | case force list of 113 | Nil -> 114 | Nothing 115 | 116 | Cons first _ -> 117 | Just first 118 | 119 | 120 | {-| Get the tail of a list. 121 | -} 122 | tail : LazyList a -> Maybe (LazyList a) 123 | tail list = 124 | case force list of 125 | Nil -> 126 | Nothing 127 | 128 | Cons _ rest -> 129 | Just rest 130 | 131 | 132 | {-| Get the head and tail of a list. 133 | -} 134 | headAndTail : LazyList a -> Maybe ( a, LazyList a ) 135 | headAndTail list = 136 | case force list of 137 | Nil -> 138 | Nothing 139 | 140 | Cons first rest -> 141 | Just ( first, rest ) 142 | 143 | 144 | {-| Repeat a value ad infinitum. 145 | Be careful when you use this. The result of this is a truly infinite list. 146 | Do not try calling `reduce` or `toList` on an infinite list as it'll never 147 | finish computing. Make sure you then filter it down to a finite list with `head` 148 | or `take` or something. 149 | -} 150 | repeat : a -> LazyList a 151 | repeat a = 152 | lazy <| 153 | \() -> 154 | Cons a (repeat a) 155 | 156 | 157 | {-| Append a list to another list. 158 | -} 159 | append : LazyList a -> LazyList a -> LazyList a 160 | append list1 list2 = 161 | lazy <| 162 | \() -> 163 | case force list1 of 164 | Nil -> 165 | force list2 166 | 167 | Cons first rest -> 168 | force (first ::: rest +++ list2) 169 | 170 | 171 | {-| Interleave the elements of a list in another list. The two lists get 172 | interleaved at the end. 173 | -} 174 | interleave : LazyList a -> LazyList a -> LazyList a 175 | interleave list1 list2 = 176 | lazy <| 177 | \() -> 178 | case force list1 of 179 | Nil -> 180 | force list2 181 | 182 | Cons first1 rest1 -> 183 | case force list2 of 184 | Nil -> 185 | force list1 186 | 187 | Cons first2 rest2 -> 188 | force (first1 ::: first2 ::: interleave rest1 rest2) 189 | 190 | 191 | {-| Places the given value between all members of the given list. 192 | -} 193 | intersperse : a -> LazyList a -> LazyList a 194 | intersperse a list = 195 | lazy <| 196 | \() -> 197 | case force list of 198 | Nil -> 199 | Nil 200 | 201 | Cons first rest -> 202 | case force rest of 203 | Nil -> 204 | force (first ::: empty) 205 | 206 | Cons second tail -> 207 | case force tail of 208 | Nil -> 209 | force (first ::: a ::: second ::: empty) 210 | 211 | _ -> 212 | force (first ::: a ::: second ::: a ::: intersperse a tail) 213 | 214 | 215 | {-| Take a list and repeat it ad infinitum. This cycles a finite list 216 | by putting the front after the end of the list. This results in a no-op in 217 | the case of an infinite list. 218 | -} 219 | cycle : LazyList a -> LazyList a 220 | cycle list = 221 | list 222 | +++ (lazy <| 223 | \() -> 224 | force (cycle list) 225 | ) 226 | 227 | 228 | {-| Create an infinite list of applications of a function on some value. 229 | 230 | Equivalent to: 231 | 232 | x ::: f x ::: f (f x) ::: f (f (f x)) ::: ... -- etc... 233 | -} 234 | iterate : (a -> a) -> a -> LazyList a 235 | iterate f a = 236 | lazy <| 237 | \() -> 238 | Cons a (iterate f (f a)) 239 | 240 | 241 | {-| The infinite list of counting numbers. 242 | 243 | i.e.: 244 | 245 | 1 ::: 2 ::: 3 ::: 4 ::: 5 ::: ... -- etc... 246 | -} 247 | numbers : LazyList number 248 | numbers = 249 | iterate ((+) 1) 1 250 | 251 | 252 | {-| Take at most `n` many values from a list. 253 | -} 254 | take : Int -> LazyList a -> LazyList a 255 | take n list = 256 | lazy <| 257 | \() -> 258 | if n <= 0 then 259 | Nil 260 | else 261 | case force list of 262 | Nil -> 263 | Nil 264 | 265 | Cons first rest -> 266 | Cons first (take (n - 1) rest) 267 | 268 | 269 | {-| Take elements from a list as long as the predicate is satisfied. 270 | -} 271 | takeWhile : (a -> Bool) -> LazyList a -> LazyList a 272 | takeWhile predicate list = 273 | lazy <| 274 | \() -> 275 | case force list of 276 | Nil -> 277 | Nil 278 | 279 | Cons first rest -> 280 | if predicate first then 281 | Cons first (takeWhile predicate rest) 282 | else 283 | Nil 284 | 285 | 286 | {-| Drop at most `n` many values from a list. 287 | -} 288 | drop : Int -> LazyList a -> LazyList a 289 | drop n list = 290 | lazy <| 291 | \() -> 292 | if n <= 0 then 293 | force list 294 | else 295 | case force list of 296 | Nil -> 297 | Nil 298 | 299 | Cons first rest -> 300 | force (drop (n - 1) rest) 301 | 302 | 303 | {-| Drop elements from a list as long as the predicate is satisfied. 304 | -} 305 | dropWhile : (a -> Bool) -> LazyList a -> LazyList a 306 | dropWhile predicate list = 307 | lazy <| 308 | \() -> 309 | case force list of 310 | Nil -> 311 | Nil 312 | 313 | Cons first rest -> 314 | if predicate first then 315 | force (dropWhile predicate rest) 316 | else 317 | force list 318 | 319 | 320 | {-| Test if a value is a member of a list. 321 | -} 322 | member : a -> LazyList a -> Bool 323 | member a list = 324 | case force list of 325 | Nil -> 326 | False 327 | 328 | Cons first rest -> 329 | first == a || member a rest 330 | 331 | 332 | {-| Get the length of a lazy list. 333 | 334 | Warning: This will not terminate if the list is infinite. 335 | -} 336 | length : LazyList a -> Int 337 | length = 338 | reduce (\_ n -> n + 1) 0 339 | 340 | 341 | {-| Remove all duplicates from a list and return a list of distinct elements. 342 | -} 343 | unique : LazyList a -> LazyList a 344 | unique list = 345 | lazy <| 346 | \() -> 347 | case force list of 348 | Nil -> 349 | Nil 350 | 351 | Cons first rest -> 352 | if member first rest then 353 | force (unique rest) 354 | else 355 | Cons first (unique rest) 356 | 357 | 358 | {-| Keep all elements in a list that satisfy the given predicate. 359 | -} 360 | keepIf : (a -> Bool) -> LazyList a -> LazyList a 361 | keepIf predicate list = 362 | lazy <| 363 | \() -> 364 | case force list of 365 | Nil -> 366 | Nil 367 | 368 | Cons first rest -> 369 | if predicate first then 370 | Cons first (keepIf predicate rest) 371 | else 372 | force (keepIf predicate rest) 373 | 374 | 375 | {-| Drop all elements in a list that satisfy the given predicate. 376 | -} 377 | dropIf : (a -> Bool) -> LazyList a -> LazyList a 378 | dropIf predicate = 379 | keepIf (\n -> not (predicate n)) 380 | 381 | 382 | {-| Map a function that may fail over a lazy list, keeping only 383 | the values that were successfully transformed. 384 | -} 385 | filterMap : (a -> Maybe b) -> LazyList a -> LazyList b 386 | filterMap transform list = 387 | lazy <| 388 | \() -> 389 | case force list of 390 | Nil -> 391 | Nil 392 | 393 | Cons first rest -> 394 | case transform first of 395 | Just val -> 396 | Cons val (filterMap transform rest) 397 | 398 | Nothing -> 399 | force (filterMap transform rest) 400 | 401 | 402 | {-| Reduce a list with a given reducer and an initial value. 403 | 404 | Example : 405 | reduce (+) 0 (1 ::: 2 ::: 3 ::: 4 ::: empty) == 10 406 | -} 407 | reduce : (a -> b -> b) -> b -> LazyList a -> b 408 | reduce reducer b list = 409 | case force list of 410 | Nil -> 411 | b 412 | 413 | Cons first rest -> 414 | reduce reducer (reducer first b) rest 415 | 416 | 417 | {-| Analogous to `List.foldl`. Is an alias for `reduce`. 418 | -} 419 | foldl : (a -> b -> b) -> b -> LazyList a -> b 420 | foldl = 421 | reduce 422 | 423 | 424 | {-| Analogous to `List.foldr`. 425 | -} 426 | foldr : (a -> b -> b) -> b -> LazyList a -> b 427 | foldr reducer b list = 428 | Array.foldr reducer b (toArray list) 429 | 430 | 431 | {-| Get the sum of a list of numbers. 432 | -} 433 | sum : LazyList number -> number 434 | sum = 435 | reduce (+) 0 436 | 437 | 438 | {-| Get the product of a list of numbers. 439 | -} 440 | product : LazyList number -> number 441 | product = 442 | reduce (*) 1 443 | 444 | 445 | {-| Flatten a list of lists into a single list by appending all the inner 446 | lists into one big list. 447 | -} 448 | flatten : LazyList (LazyList a) -> LazyList a 449 | flatten list = 450 | lazy <| 451 | \() -> 452 | case force list of 453 | Nil -> 454 | Nil 455 | 456 | Cons first rest -> 457 | force (first +++ flatten rest) 458 | 459 | 460 | {-| Chain list producing operations. Map then flatten. 461 | -} 462 | andThen : (a -> LazyList b) -> LazyList a -> LazyList b 463 | andThen f list = 464 | map f list |> flatten 465 | 466 | 467 | {-| Reverse a list. 468 | -} 469 | reverse : LazyList a -> LazyList a 470 | reverse = 471 | reduce cons empty 472 | 473 | 474 | {-| Map a function to a list. 475 | -} 476 | map : (a -> b) -> LazyList a -> LazyList b 477 | map f list = 478 | lazy <| 479 | \() -> 480 | case force list of 481 | Nil -> 482 | Nil 483 | 484 | Cons first rest -> 485 | Cons (f first) (map f rest) 486 | 487 | 488 | {-| -} 489 | map2 : (a -> b -> c) -> LazyList a -> LazyList b -> LazyList c 490 | map2 f list1 list2 = 491 | lazy <| 492 | \() -> 493 | case force list1 of 494 | Nil -> 495 | Nil 496 | 497 | Cons first1 rest1 -> 498 | case force list2 of 499 | Nil -> 500 | Nil 501 | 502 | Cons first2 rest2 -> 503 | Cons (f first1 first2) (map2 f rest1 rest2) 504 | 505 | 506 | {-| Known as `mapN` in some circles. Allows you to apply `map` in cases 507 | where then number of arguments are greater than 5. 508 | 509 | The argument order is such that it works well with `|>` chains. 510 | -} 511 | andMap : LazyList a -> LazyList (a -> b) -> LazyList b 512 | andMap listVal listFuncs = 513 | map2 (<|) listFuncs listVal 514 | 515 | 516 | {-| -} 517 | map3 : (a -> b -> c -> d) -> LazyList a -> LazyList b -> LazyList c -> LazyList d 518 | map3 f list1 list2 list3 = 519 | lazy <| 520 | \() -> 521 | case force list1 of 522 | Nil -> 523 | Nil 524 | 525 | Cons first1 rest1 -> 526 | case force list2 of 527 | Nil -> 528 | Nil 529 | 530 | Cons first2 rest2 -> 531 | case force list3 of 532 | Nil -> 533 | Nil 534 | 535 | Cons first3 rest3 -> 536 | Cons (f first1 first2 first3) (map3 f rest1 rest2 rest3) 537 | 538 | 539 | {-| -} 540 | map4 : (a -> b -> c -> d -> e) -> LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e 541 | map4 f list1 list2 list3 list4 = 542 | lazy <| 543 | \() -> 544 | case force list1 of 545 | Nil -> 546 | Nil 547 | 548 | Cons first1 rest1 -> 549 | case force list2 of 550 | Nil -> 551 | Nil 552 | 553 | Cons first2 rest2 -> 554 | case force list3 of 555 | Nil -> 556 | Nil 557 | 558 | Cons first3 rest3 -> 559 | case force list4 of 560 | Nil -> 561 | Nil 562 | 563 | Cons first4 rest4 -> 564 | Cons (f first1 first2 first3 first4) (map4 f rest1 rest2 rest3 rest4) 565 | 566 | 567 | {-| -} 568 | map5 : (a -> b -> c -> d -> e -> f) -> LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e -> LazyList f 569 | map5 f list1 list2 list3 list4 list5 = 570 | lazy <| 571 | \() -> 572 | case force list1 of 573 | Nil -> 574 | Nil 575 | 576 | Cons first1 rest1 -> 577 | case force list2 of 578 | Nil -> 579 | Nil 580 | 581 | Cons first2 rest2 -> 582 | case force list3 of 583 | Nil -> 584 | Nil 585 | 586 | Cons first3 rest3 -> 587 | case force list4 of 588 | Nil -> 589 | Nil 590 | 591 | Cons first4 rest4 -> 592 | case force list5 of 593 | Nil -> 594 | Nil 595 | 596 | Cons first5 rest5 -> 597 | Cons 598 | (f first1 first2 first3 first4 first5) 599 | (map5 f rest1 rest2 rest3 rest4 rest5) 600 | 601 | 602 | {-| -} 603 | zip : LazyList a -> LazyList b -> LazyList ( a, b ) 604 | zip = 605 | map2 (,) 606 | 607 | 608 | {-| -} 609 | zip3 : LazyList a -> LazyList b -> LazyList c -> LazyList ( a, b, c ) 610 | zip3 = 611 | map3 (,,) 612 | 613 | 614 | {-| -} 615 | zip4 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList ( a, b, c, d ) 616 | zip4 = 617 | map4 (,,,) 618 | 619 | 620 | {-| -} 621 | zip5 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e -> LazyList ( a, b, c, d, e ) 622 | zip5 = 623 | map5 (,,,,) 624 | 625 | 626 | {-| Create a lazy list containing all possible pairs in the given lazy lists. 627 | -} 628 | product2 : LazyList a -> LazyList b -> LazyList ( a, b ) 629 | product2 list1 list2 = 630 | lazy <| 631 | \() -> 632 | case force list1 of 633 | Nil -> 634 | Nil 635 | 636 | Cons first1 rest1 -> 637 | case force list2 of 638 | Nil -> 639 | Nil 640 | 641 | Cons _ _ -> 642 | force <| map ((,) first1) list2 +++ product2 rest1 list2 643 | 644 | 645 | {-| Create a lazy list containing all possible triples in the given lazy lists. 646 | -} 647 | product3 : LazyList a -> LazyList b -> LazyList c -> LazyList ( a, b, c ) 648 | product3 list1 list2 list3 = 649 | lazy <| 650 | \() -> 651 | case force list1 of 652 | Nil -> 653 | Nil 654 | 655 | Cons first1 rest1 -> 656 | force <| map (\( b, c ) -> ( first1, b, c )) (product2 list2 list3) +++ product3 rest1 list2 list3 657 | 658 | 659 | {-| Create a lazy list containing all possible 4-tuples in the given lazy lists. 660 | -} 661 | product4 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList ( a, b, c, d ) 662 | product4 list1 list2 list3 list4 = 663 | lazy <| 664 | \() -> 665 | case force list1 of 666 | Nil -> 667 | Nil 668 | 669 | Cons first1 rest1 -> 670 | force <| map (\( b, c, d ) -> ( first1, b, c, d )) (product3 list2 list3 list4) +++ product4 rest1 list2 list3 list4 671 | 672 | 673 | {-| Create a lazy list containing all possible 5-tuples in the given lazy lists. 674 | -} 675 | product5 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e -> LazyList ( a, b, c, d, e ) 676 | product5 list1 list2 list3 list4 list5 = 677 | lazy <| 678 | \() -> 679 | case force list1 of 680 | Nil -> 681 | Nil 682 | 683 | Cons first1 rest1 -> 684 | force <| map (\( b, c, d, e ) -> ( first1, b, c, d, e )) (product4 list2 list3 list4 list5) +++ product5 rest1 list2 list3 list4 list5 685 | 686 | 687 | {-| Convert a lazy list to a normal list. 688 | -} 689 | toList : LazyList a -> List a 690 | toList list = 691 | case force list of 692 | Nil -> 693 | [] 694 | 695 | Cons first rest -> 696 | first :: toList rest 697 | 698 | 699 | {-| Convert a normal list to a lazy list. 700 | -} 701 | fromList : List a -> LazyList a 702 | fromList = 703 | List.foldr cons empty 704 | 705 | 706 | {-| Convert a lazy list to an array. 707 | -} 708 | toArray : LazyList a -> Array a 709 | toArray list = 710 | case force list of 711 | Nil -> 712 | Array.empty 713 | 714 | Cons first rest -> 715 | Array.append (Array.push first Array.empty) (toArray rest) 716 | 717 | 718 | {-| Convert an array to a lazy list. 719 | -} 720 | fromArray : Array a -> LazyList a 721 | fromArray = 722 | Array.foldr cons empty 723 | 724 | 725 | 726 | --------------------- 727 | -- INFIX OPERATORS -- 728 | --------------------- 729 | 730 | 731 | infixr 5 ::: 732 | 733 | 734 | {-| Alias for `cons`. Analogous to `::` for lists. 735 | -} 736 | (:::) : a -> LazyList a -> LazyList a 737 | (:::) = 738 | cons 739 | 740 | 741 | infixr 5 +++ 742 | 743 | 744 | {-| Alias for `append`. Analogous to `++` for lists. 745 | -} 746 | (+++) : LazyList a -> LazyList a -> LazyList a 747 | (+++) = 748 | append 749 | --------------------------------------------------------------------------------