datatypes.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /*
  2. * Quick-n-dirty algebraic datatypes.
  3. *
  4. * These let us handle the possibility of failure without having to constantly write code to check for it.
  5. * We can apply all of the transformations we need as if the data is present using `map`.
  6. * If there's a None, or a FetchError, or a Pending, those are left untouched.
  7. *
  8. * I've used perhaps an odd bit of terminology from scalaz in `fold`. This is basically a `switch` statement:
  9. * You pass it a set of functions to handle the various different states of the datatype, and if it finds the
  10. * function it'll call it on its value.
  11. *
  12. * It's handy to have this in functional style when dealing with React as we can dispatch different ways of rendering
  13. * really simply:
  14. * ```
  15. * bundleFetchStatus.fold({
  16. * some: (fetchStatus) => <ProgressBar fetchsStatus={fetchStatus} />,
  17. * }),
  18. * ```
  19. */
  20. class Optional {
  21. static from(value) {
  22. return (value && Some.of(value)) || None;
  23. }
  24. map(f) {
  25. return this;
  26. }
  27. flatMap(f) {
  28. return this;
  29. }
  30. fold({ none }) {
  31. return none && none();
  32. }
  33. }
  34. class Some extends Optional {
  35. constructor(value) {
  36. super();
  37. this.value = value;
  38. }
  39. map(f) {
  40. return Some.of(f(this.value));
  41. }
  42. flatMap(f) {
  43. return f(this.value);
  44. }
  45. fold({ some }) {
  46. return some && some(this.value);
  47. }
  48. static of(value) {
  49. return new Some(value);
  50. }
  51. }
  52. const None = new Optional();
  53. class FetchStatus {
  54. constructor(opt = {}) {
  55. this.opt = { at: Date.now(), ...opt };
  56. }
  57. map(f) {
  58. return this;
  59. }
  60. flatMap(f) {
  61. return this;
  62. }
  63. }
  64. class Success extends FetchStatus {
  65. static of(value) {
  66. return new Success(value);
  67. }
  68. constructor(value, opt) {
  69. super(opt);
  70. this.value = value;
  71. }
  72. map(f) {
  73. return new Success(f(this.value), this.opt);
  74. }
  75. flatMap(f) {
  76. return f(this.value, this.opt);
  77. }
  78. fold({ success }) {
  79. return success instanceof Function ? success(this.value, this.opt) : undefined;
  80. }
  81. }
  82. class Pending extends FetchStatus {
  83. static of(opt) {
  84. return new Pending(opt);
  85. }
  86. constructor(opt) {
  87. super(opt);
  88. }
  89. fold({ pending }) {
  90. return pending instanceof Function ? pending(this.opt) : undefined;
  91. }
  92. }
  93. class FetchError extends FetchStatus {
  94. static of(reason, opt) {
  95. return new FetchError(reason, opt);
  96. }
  97. constructor(reason, opt) {
  98. super(opt);
  99. this.reason = reason;
  100. }
  101. fold({ error }) {
  102. return error instanceof Function ? error(this.reason, this.opt) : undefined;
  103. }
  104. }