Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add polymorphic type catamorphisms #280

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,116 @@ 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/' 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
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`.

For example
```js
function Id(x) {
this.x = x;
}

Id.prototype['fantasy-land/identity'] = function identity(f) {
return f(this.x);
};

Array.prototype['fantasy-land/identity'] = function identity(f) {
return f(this[0]);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's safe to define Array#fantasy-land/identity since [] exists.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct David. Perhaps identity isn't the right example to use.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[] :: Identity Undefined :p

Copy link
Member Author

@gabejohnson gabejohnson Feb 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

function Some(x) {
  this.x = x;
};
var None = {};

Some.prototype['fantasy-land/maybe'] = function maybe(_, f) {
  return f(this.x);
};
None['fantasy-land/maybe'] = function maybe(d, _) {
  return d;
};

Array.prototype['fantasy-land/maybe'] = function maybe(d, f) {
  if (this.length === 0) return d;
  return f(this[0]);
};

function maybe(d, f, m) {
  return m['fantasy-land/maybe'](d, f);
}

var head = maybe.bind(null, None, Some);

var none = head([]);
none // None

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

?
Edit: Added maybe and fromMaybe for clarity

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that example, Gabe. 👍


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)

1. `x` is the default value in the `Nothing` case

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

```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 unary function. It is called in the `Left` case

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 unary function. It is called in the `Right` case

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

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

When creating data types which satisfy multiple algebras, authors may choose
Expand Down