Appendix B: Algebraic Structures Support

    Either

    1. constructor(x) {
    2. this.$value = x;
    3. }
    4. // ----- Pointed (Either a)
    5. static of(x) {
    6. return new Right(x);
    7. }
    8. }
    9. class Left extends Either {
    10. get isLeft() {
    11. return true;
    12. }
    13. get isRight() {
    14. return false;
    15. }
    16. static of(x) {
    17. throw new Error('`of` called on class Left (value) instead of Either (type)');
    18. }
    19. inspect() {
    20. return `Left(${inspect(this.$value)})`;
    21. }
    22. // ----- Functor (Either a)
    23. map() {
    24. return this;
    25. }
    26. // ----- Applicative (Either a)
    27. ap() {
    28. return this;
    29. }
    30. // ----- Monad (Either a)
    31. chain() {
    32. return this;
    33. }
    34. join() {
    35. return this;
    36. }
    37. // ----- Traversable (Either a)
    38. sequence(of) {
    39. return of(this);
    40. }
    41. traverse(of, fn) {
    42. return of(this);
    43. }
    44. }
    45. class Right extends Either {
    46. get isLeft() {
    47. return false;
    48. }
    49. get isRight() {
    50. return true;
    51. static of(x) {
    52. throw new Error('`of` called on class Right (value) instead of Either (type)');
    53. }
    54. inspect() {
    55. return `Right(${inspect(this.$value)})`;
    56. // ----- Functor (Either a)
    57. map(fn) {
    58. return Either.of(fn(this.$value));
    59. }
    60. // ----- Applicative (Either a)
    61. ap(f) {
    62. return f.map(this.$value);
    63. }
    64. // ----- Monad (Either a)
    65. chain(fn) {
    66. return fn(this.$value);
    67. }
    68. join() {
    69. return this.$value;
    70. }
    71. // ----- Traversable (Either a)
    72. sequence(of) {
    73. return this.traverse(of, identity);
    74. }
    75. traverse(of, fn) {
    76. fn(this.$value).map(Either.of);
    77. }
    78. }

    IO

    1. class IO {
    2. constructor(fn) {
    3. this.unsafePerformIO = fn;
    4. }
    5. inspect() {
    6. return `IO(?)`;
    7. }
    8. // ----- Pointed IO
    9. static of(x) {
    10. return new IO(() => x);
    11. }
    12. // ----- Functor IO
    13. map(fn) {
    14. return new IO(compose(fn, this.unsafePerformIO));
    15. }
    16. // ----- Applicative IO
    17. ap(f) {
    18. return this.chain(fn => f.map(fn));
    19. }
    20. // ----- Monad IO
    21. chain(fn) {
    22. return this.map(fn).join();
    23. }
    24. join() {
    25. return this.unsafePerformIO();
    26. }
    27. }

    Map

    1. class Map {
    2. constructor(x) {
    3. }
    4. inspect() {
    5. return `Map(${inspect(this.$value)})`;
    6. }
    7. const singleton = {};
    8. singleton[k] = v;
    9. return Map.of(Object.assign({}, this.$value, singleton));
    10. }
    11. reduceWithKeys(fn, zero) {
    12. return Object.keys(this.$value)
    13. .reduce((acc, k) => fn(acc, this.$value[k], k), zero);
    14. }
    15. // ----- Functor (Map a)
    16. map(fn) {
    17. return this.reduceWithKeys(
    18. (m, v, k) => m.insert(k, fn(v)),
    19. new Map({}),
    20. );
    21. }
    22. // ----- Traversable (Map a)
    23. sequence(of) {
    24. return this.traverse(of, identity);
    25. }
    26. traverse(of, fn) {
    27. return this.reduceWithKeys(
    28. (f, a, k) => fn(a).map(b => m => m.insert(k, b)).ap(f),
    29. of(new Map({})),
    30. );
    31. }
    32. }

    Task

    1. class Task {
    2. constructor(fork) {
    3. this.fork = fork;
    4. }
    5. inspect() {
    6. return 'Task(?)';
    7. }
    8. static rejected(x) {
    9. return new Task((reject, _) => reject(x));
    10. }
    11. // ----- Pointed (Task a)
    12. static of(x) {
    13. return new Task((_, resolve) => resolve(x));
    14. }
    15. // ----- Functor (Task a)
    16. map(fn) {
    17. return new Task((reject, resolve) => this.fork(reject, compose(resolve, fn)));
    18. }
    19. // ----- Applicative (Task a)
    20. ap(f) {
    21. return this.chain(fn => f.map(fn));
    22. }
    23. // ----- Monad (Task a)
    24. chain(fn) {
    25. return new Task((reject, resolve) => this.fork(reject, x => fn(x).fork(reject, resolve)));
    26. }
    27. join() {
    28. return this.chain(identity);
    29. }