├── .gitignore ├── README.md ├── elm-package.json └── src └── Lazy └── List.elm /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lazy list implementation in Elm 2 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.3.0", 3 | "summary": "Lazy list implementation in Elm", 4 | "repository": "https://github.com/elm-community/elm-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": "4.0.0 <= v < 5.0.0", 14 | "elm-lang/lazy": "1.0.0 <= v < 2.0.0" 15 | }, 16 | "elm-version": "0.17.0 <= v < 0.18.0" 17 | } -------------------------------------------------------------------------------- /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, flatMap, 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 first `member` 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 | {-| Map then flatten. 461 | -} 462 | flatMap : (a -> LazyList b) -> LazyList a -> LazyList b 463 | flatMap f = 464 | map f >> flatten 465 | 466 | 467 | {-| Chain list producing operations. 468 | -} 469 | andThen : LazyList a -> (a -> LazyList b) -> LazyList b 470 | andThen = 471 | flip flatMap 472 | 473 | 474 | {-| Reverse a list. 475 | -} 476 | reverse : LazyList a -> LazyList a 477 | reverse = 478 | reduce cons empty 479 | 480 | 481 | {-| Map a function to a list. 482 | -} 483 | map : (a -> b) -> LazyList a -> LazyList b 484 | map f list = 485 | lazy <| 486 | \() -> 487 | case force list of 488 | Nil -> 489 | Nil 490 | 491 | Cons first rest -> 492 | Cons (f first) (map f rest) 493 | 494 | 495 | {-| -} 496 | map2 : (a -> b -> c) -> LazyList a -> LazyList b -> LazyList c 497 | map2 f list1 list2 = 498 | lazy <| 499 | \() -> 500 | case force list1 of 501 | Nil -> 502 | Nil 503 | 504 | Cons first1 rest1 -> 505 | case force list2 of 506 | Nil -> 507 | Nil 508 | 509 | Cons first2 rest2 -> 510 | Cons (f first1 first2) (map2 f rest1 rest2) 511 | 512 | 513 | {-| Known as `mapN` in some circles. Allows you to apply `map` in cases 514 | where then number of arguments are greater than 5. 515 | -} 516 | andMap : LazyList (a -> b) -> LazyList a -> LazyList b 517 | andMap = 518 | map2 (<|) 519 | 520 | 521 | {-| -} 522 | map3 : (a -> b -> c -> d) -> LazyList a -> LazyList b -> LazyList c -> LazyList d 523 | map3 f l1 l2 l3 = 524 | f 525 | `map` l1 526 | `andMap` l2 527 | `andMap` l3 528 | 529 | 530 | {-| -} 531 | map4 : (a -> b -> c -> d -> e) -> LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e 532 | map4 f l1 l2 l3 l4 = 533 | f 534 | `map` l1 535 | `andMap` l2 536 | `andMap` l3 537 | `andMap` l4 538 | 539 | 540 | {-| -} 541 | map5 : (a -> b -> c -> d -> e -> f) -> LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e -> LazyList f 542 | map5 f l1 l2 l3 l4 l5 = 543 | f 544 | `map` l1 545 | `andMap` l2 546 | `andMap` l3 547 | `andMap` l4 548 | `andMap` l5 549 | 550 | 551 | {-| -} 552 | zip : LazyList a -> LazyList b -> LazyList ( a, b ) 553 | zip = 554 | map2 (,) 555 | 556 | 557 | {-| -} 558 | zip3 : LazyList a -> LazyList b -> LazyList c -> LazyList ( a, b, c ) 559 | zip3 = 560 | map3 (,,) 561 | 562 | 563 | {-| -} 564 | zip4 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList ( a, b, c, d ) 565 | zip4 = 566 | map4 (,,,) 567 | 568 | 569 | {-| -} 570 | zip5 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e -> LazyList ( a, b, c, d, e ) 571 | zip5 = 572 | map5 (,,,,) 573 | 574 | 575 | {-| Create a lazy list containing all possible pairs in the given lazy lists. 576 | -} 577 | product2 : LazyList a -> LazyList b -> LazyList ( a, b ) 578 | product2 list1 list2 = 579 | lazy <| 580 | \() -> 581 | case force list1 of 582 | Nil -> 583 | Nil 584 | 585 | Cons first1 rest1 -> 586 | case force list2 of 587 | Nil -> 588 | Nil 589 | 590 | Cons _ _ -> 591 | force <| map ((,) first1) list2 +++ product2 rest1 list2 592 | 593 | 594 | {-| Create a lazy list containing all possible triples in the given lazy lists. 595 | -} 596 | product3 : LazyList a -> LazyList b -> LazyList c -> LazyList ( a, b, c ) 597 | product3 list1 list2 list3 = 598 | lazy <| 599 | \() -> 600 | case force list1 of 601 | Nil -> 602 | Nil 603 | 604 | Cons first1 rest1 -> 605 | force <| map (\( b, c ) -> ( first1, b, c )) (product2 list2 list3) +++ product3 rest1 list2 list3 606 | 607 | 608 | {-| Create a lazy list containing all possible 4-tuples in the given lazy lists. 609 | -} 610 | product4 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList ( a, b, c, d ) 611 | product4 list1 list2 list3 list4 = 612 | lazy <| 613 | \() -> 614 | case force list1 of 615 | Nil -> 616 | Nil 617 | 618 | Cons first1 rest1 -> 619 | force <| map (\( b, c, d ) -> ( first1, b, c, d )) (product3 list2 list3 list4) +++ product4 rest1 list2 list3 list4 620 | 621 | 622 | {-| Create a lazy list containing all possible 5-tuples in the given lazy lists. 623 | -} 624 | product5 : LazyList a -> LazyList b -> LazyList c -> LazyList d -> LazyList e -> LazyList ( a, b, c, d, e ) 625 | product5 list1 list2 list3 list4 list5 = 626 | lazy <| 627 | \() -> 628 | case force list1 of 629 | Nil -> 630 | Nil 631 | 632 | Cons first1 rest1 -> 633 | force <| map (\( b, c, d, e ) -> ( first1, b, c, d, e )) (product4 list2 list3 list4 list5) +++ product5 rest1 list2 list3 list4 list5 634 | 635 | 636 | {-| Convert a lazy list to a normal list. 637 | -} 638 | toList : LazyList a -> List a 639 | toList list = 640 | case force list of 641 | Nil -> 642 | [] 643 | 644 | Cons first rest -> 645 | first :: toList rest 646 | 647 | 648 | {-| Convert a normal list to a lazy list. 649 | -} 650 | fromList : List a -> LazyList a 651 | fromList = 652 | List.foldr cons empty 653 | 654 | 655 | {-| Convert a lazy list to an array. 656 | -} 657 | toArray : LazyList a -> Array a 658 | toArray list = 659 | case force list of 660 | Nil -> 661 | Array.empty 662 | 663 | Cons first rest -> 664 | Array.append (Array.push first Array.empty) (toArray rest) 665 | 666 | 667 | {-| Convert an array to a lazy list. 668 | -} 669 | fromArray : Array a -> LazyList a 670 | fromArray = 671 | Array.foldr cons empty 672 | 673 | 674 | 675 | --------------------- 676 | -- INFIX OPERATORS -- 677 | --------------------- 678 | 679 | 680 | infixr 5 ::: 681 | 682 | 683 | {-| Alias for `cons`. Analogous to `::` for lists. 684 | -} 685 | (:::) : a -> LazyList a -> LazyList a 686 | (:::) = 687 | cons 688 | 689 | 690 | infixr 5 +++ 691 | 692 | 693 | {-| Alias for `append`. Analogous to `++` for lists. 694 | -} 695 | (+++) : LazyList a -> LazyList a -> LazyList a 696 | (+++) = 697 | append 698 | --------------------------------------------------------------------------------