| 1 | [![Build Status](https://travis-ci.org/ibnfirnas/hope.svg?branch=master)](https://travis-ci.org/ibnfirnas/hope) |
| 2 | |
| 3 | Hope |
| 4 | ==== |
| 5 | |
| 6 | A quest for a "standard" library with uniform, composable abstractions. |
| 7 | |
| 8 | Originally motivated by a desire for an error monad and generic option type |
| 9 | operations, and stood for _Higher Order Programming in Erlang_. Soon after, I |
| 10 | wished all standard containers used consistent conventions and protocols (such |
| 11 | as consistent accessor names, argument positioning rules and expression of |
| 12 | semantics with option and result types). |
| 13 | |
| 14 | Here lies an experiment to see what something like that could look like. As all |
| 15 | proper experiments should, this one is used daily in production projects (hence |
| 16 | the high-ish version number, 'cause semver). |
| 17 | |
| 18 | |
| 19 | Conventions |
| 20 | ----------- |
| 21 | |
| 22 | I entertain any forward-thinking library design ideas, but more than anything |
| 23 | else, these are influenced by Jane Street's Core of the OCaml world. |
| 24 | |
| 25 | - A module per data type implementation |
| 26 | - Name of the module is the name of the type |
| 27 | - Inside the module, the type it implements is always named t(..), such as: |
| 28 | `hope_foo:t()`, _not_ `hope_foo:foo()` |
| 29 | - t(..) is always the first argument |
| 30 | - Names of private records _may_ be short, such as: `#foo{}` or `#t{}` (Though |
| 31 | I'm second-guessing this idea, since seeing `{t, ..}` in stack traces is less |
| 32 | than helpful. I'm considering requiring fully-qualified names for all record |
| 33 | definitions and maybe short-handing what would've been `#t{..}` as |
| 34 | `-define(T, ?MODULE). -record(?T, {..}).`, which may be a bit ugly. Still |
| 35 | thinking...) |
| 36 | - Names of public records _must_ be fully qualified, such as: `#hope_module_record{}` |
| 37 | - Names of all modules _must_ be fully qualified, such as: `hope_module` (this |
| 38 | should go without saying, but just to be sure...) |
| 39 | - Keep the number of (anonymous) arguments "reasonably" low: |
| 40 | + up to 3 is normal |
| 41 | + 4 is suspicious but may be reasonable |
| 42 | + 5 is _very_ suspicious and probably unnecessary |
| 43 | + more than 5 is unacceptable, so consider reducing by: |
| 44 | 1. revising abstractions, or, if not practical |
| 45 | 2. creating a public record specifically for the purpose of passing |
| 46 | many arguents, which simulates labeled arguments. For an example see |
| 47 | https://github.com/ibnfirnas/oauth1_core where I used that technique |
| 48 | extensively (especially in oauth1_server.erl) |
| 49 | |
| 50 | |
| 51 | Abstractions |
| 52 | ------------ |
| 53 | |
| 54 | ### Monads |
| 55 | |
| 56 | A class of burritos, used for sequencing operations on a particular data type. |
| 57 | Defined in `hope_gen_monad`, implemented as: |
| 58 | |
| 59 | - `hope_result`: for composition of common functions returning |
| 60 | `{ok, Val} | {error, Reason}`. An alternative to exceptions, which makes the |
| 61 | error conditions apparent in the spec/signature. Analogous to Haskell's |
| 62 | `Data.Either a b`, Jane Street Core's (OCaml) `('a, 'b) Result.t`, Rust's |
| 63 | `Result<T, E>` |
| 64 | - `hope_option`: for expressing and composing the intention that the value may |
| 65 | or may not be available. An alternative to the common `undefined` (which is |
| 66 | equivalent to the dreaded `null`). Analogous to ML's (SML, OCaml, etc) |
| 67 | `'a Option.t`, Rust's `Option<T>` and Haskell's `Data.Maybe a` [1]. |
| 68 | |
| 69 | |
| 70 | ### Containers |
| 71 | |
| 72 | A class of abstract data types to which we have exclusive access and can put |
| 73 | things in and take them out. See issue #9 |
| 74 | |
| 75 | - Operations on all abstract types of containers _should_ share a common lexicon |
| 76 | - Concrete implementations of an abstract data type _must_ be swapable |
| 77 | |
| 78 | #### Dictionary |
| 79 | |
| 80 | Defined in `hope_gen_dictionary`, implemented as: |
| 81 | |
| 82 | - `hope_kv_list`. Equivalent to orddict/proplist. Operations implemented with |
| 83 | BIFs from `lists` module, where possible |
| 84 | |
| 85 | TBD: |
| 86 | - `hope_hash_tbl`. API around stdlib's `dict` |
| 87 | - `hope_gb_dict`. API around stdlib's `gb_trees` |
| 88 | |
| 89 | #### Set |
| 90 | |
| 91 | TBD: |
| 92 | - `hope_hash_set`. API around stdlib's `sets` |
| 93 | - `hope_gb_set`. API around stdlib's `gb_sets` |
| 94 | |
| 95 | #### Queue |
| 96 | |
| 97 | TBD |
| 98 | |
| 99 | Should include both FIFO (queue) and LIFO (stack), so that user can swap if a |
| 100 | different order is desired. |
| 101 | |
| 102 | Should we attempt to include priority queues or make them a separate abstract |
| 103 | type? |
| 104 | |
| 105 | #### Sequence |
| 106 | |
| 107 | TBD |
| 108 | |
| 109 | Not yet defined and only partially implemented as: |
| 110 | |
| 111 | - `hope_list` |
| 112 | |
| 113 | |
| 114 | ### Resources |
| 115 | |
| 116 | A class of abstract systems to which we share access with an unknown number of |
| 117 | users and can make requests to perform operations which may not get done for |
| 118 | any number of reasons. |
| 119 | |
| 120 | #### Storage |
| 121 | |
| 122 | TBD |
| 123 | |
| 124 | See issue #11 |
| 125 | |
| 126 | |
| 127 | [1]: http://en.wikipedia.org/wiki/Option_type |