From 66870336d7269e57fb20c48d3713e5bc4a58f958 Mon Sep 17 00:00:00 2001 From: Gabe Johnson Date: Wed, 10 Jan 2018 08:53:20 -0600 Subject: [PATCH 1/3] Add `Either` type specification --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index c8a2dce..0ae355a 100644 --- a/README.md +++ b/README.md @@ -796,6 +796,54 @@ The `profunctor` method takes two arguments: 3. `promap` must return a value of the same Profunctor +## Types + +In Fantasy Land, types are specified by a capitalized name and a catamorphic +method of the same name, without capitalization. The method allows access to +the values contained within the structure or the ability to provide a default +value when the structure is nullary. When defined on a value, a method name +contains the same 'fantasy-land/' prefix. + +Additionally, data constructors may be referenced in the specification by name +and arity. Conforming data structures are not required to provide these +constructors nor are any provided constructors required to share these names. +For example, instead of `Left` and `Right`, the constructors could be named +`Failure` and `Success`. + +### Either + +The `Either` type encodes the concept of binary possibility (Left a and Right b). + +If e has constructors unary constructors `Left` and `Right` + +e.either(Left, Right) === e (identity) + +#### `either` method + +```hs +either :: Either e => e a b ~> ((a -> c), (b -> c)) -> c +``` + +A value which conforms to the Either specification must provide an `either` method. + +The `either` method takes two arguments: + + e.either(f, g) + +1. `f` must be a function which returns a value + + 1. If `f` is not a function, the behaviour of `either` is unspecified. + 2. `f` can return any value. + 3. No parts of `f`'s return value should be checked. + +2. `g` must be a function which returns a value + + 1. If `g` is not a function, the behaviour of `either` is unspecified. + 2. `g` can return any value. + 3. No parts of `g`'s return value should be checked. + +3. `f`, `g`, and `either` must return a value of the same type + ## Derivations When creating data types which satisfy multiple algebras, authors may choose From 010c84fd7e6012a85443a0c445870687f2bdf51c Mon Sep 17 00:00:00 2001 From: Gabe Johnson Date: Wed, 17 Jan 2018 15:05:28 -0600 Subject: [PATCH 2/3] Update to contrast more directly with #281 --- README.md | 90 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0ae355a..73eddad 100644 --- a/README.md +++ b/README.md @@ -802,7 +802,12 @@ In Fantasy Land, types are specified by a capitalized name and a catamorphic method of the same name, without capitalization. The method allows access to the values contained within the structure or the ability to provide a default value when the structure is nullary. When defined on a value, a method name -contains the same 'fantasy-land/' prefix. +contains the same 'fantasy-land/' as other methods described in the spec prefix. + +For example +```hs +identity :: Identity i => i a ~> (a -> b) -> b +``` Additionally, data constructors may be referenced in the specification by name and arity. Conforming data structures are not required to provide these @@ -810,13 +815,54 @@ constructors nor are any provided constructors required to share these names. For example, instead of `Left` and `Right`, the constructors could be named `Failure` and `Success`. -### Either +For example +```js +function Id(x) { + this.x = x; +} -The `Either` type encodes the concept of binary possibility (Left a and Right b). +Id.prototype['fantasy-land/identity'] = function identity(f) { + return f(this.x); +}; + +Array.prototype['fantasy-land/identity'] = function identity(f) { + return f(this[0]); +}; + +var identity = 'fantasy-land/identity'; + +(new Id(42))[identity](x => x) === [42][identity](x => x); +``` + +### Maybe + +The `Maybe` type encodes the concept of optionality (Nothing and Just a). + +#### `maybe` method + +```hs +maybe :: Maybe m => m a ~> (b, (a -> b)) -> b +``` + +A value which conforms to the Maybe specification must provide a `maybe` method. + +The `maybe` method takes two arguments: + + m.maybe(x, f) -If e has constructors unary constructors `Left` and `Right` +1. `x` is the default value in the `Nothing` case -e.either(Left, Right) === e (identity) + 1. If `x` does not match the return value of `maybe`, the behaviour of + `maybe` is unspecified. + +2. `f` must be a unary function. It is called in the `Just` case + + 1. If `f` is not a unary function or the return value of `f` does not match + the return value of `maybe`, the behaviour of `maybe` is unspecified. + +### Either + +The `Either` type encodes the concept of binary possibility (Left a and Right b). #### `either` method @@ -830,19 +876,35 @@ The `either` method takes two arguments: e.either(f, g) -1. `f` must be a function which returns a value +1. `f` must be a unary function. It is called in the `Left` case - 1. If `f` is not a function, the behaviour of `either` is unspecified. - 2. `f` can return any value. - 3. No parts of `f`'s return value should be checked. + 1. If `f` is not a unary function or the return value of `f` does not match + the return value of `either`, the behaviour of `either` is unspecified. -2. `g` must be a function which returns a value +2. `g` must be a unary function. It is called in the `Right` case - 1. If `g` is not a function, the behaviour of `either` is unspecified. - 2. `g` can return any value. - 3. No parts of `g`'s return value should be checked. + 1. If `g` is not a unary function or the return value of `g` does not match + the return value of `either`, the behaviour of `either` is unspecified. + +### Pair + +`Pair` is the canonical product type and represents a structure containing two +values (Pair a b). + +```hs +pair :: Pair p => p a b ~> ((a, b) -> c) -> c +``` + +A value which conforms to the Pair specification must provide a `pair` method. + +The `pair` method takes a single argument: + + p.pair(f) + +1. `f` must be a binary function -3. `f`, `g`, and `either` must return a value of the same type + 1. If `f` is not a binary function or the return value of `f` does not match + the return value of `pair`, the behaviour of `pair` is unspecified. ## Derivations From e8b16788cf52b3d442ff79a12b8cf9f599543045 Mon Sep 17 00:00:00 2001 From: Gabe Johnson Date: Fri, 16 Feb 2018 16:02:33 -0600 Subject: [PATCH 3/3] Change example `Identity` -> `Maybe` --- README.md | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 73eddad..7dd42e4 100644 --- a/README.md +++ b/README.md @@ -812,26 +812,46 @@ identity :: Identity i => i a ~> (a -> b) -> b Additionally, data constructors may be referenced in the specification by name and arity. Conforming data structures are not required to provide these constructors nor are any provided constructors required to share these names. -For example, instead of `Left` and `Right`, the constructors could be named -`Failure` and `Success`. +Instead of `Just` and `Nothing`, the constructors could be named `Some` and +`None`. For example ```js -function Id(x) { +function Some(x) { + if (!(this instanceof Some)) return new Some(x); this.x = x; -} - -Id.prototype['fantasy-land/identity'] = function identity(f) { +}; +Some.prototype['fantasy-land/maybe'] = function maybe(_, f) { return f(this.x); }; -Array.prototype['fantasy-land/identity'] = function identity(f) { - return f(this[0]); +var None = { + 'fantasy-land/maybe': function maybe(d, _) { + return d; + } +}; + +Array.prototype['fantasy-land/maybe'] = function maybe(d, f) { + return this.length === 0 ? d : f(this[0]); }; -var identity = 'fantasy-land/identity'; +function maybe(d, f, m) { + return m['fantasy-land/maybe'](d, f); +} + +var head = maybe.bind(null, None, Some); + +var none = head([]); +none // None -(new Id(42))[identity](x => x) === [42][identity](x => x); +var some = head([1, 2, 3]); +some // Some(1); + +function fromMaybe(d, m) { + return maybe(d, x => x, m); +} +fromMaybe(0, none) // 0 +fromMaybe(0, some) // 1 ``` ### Maybe