Skip to content
This repository has been archived by the owner on Sep 9, 2023. It is now read-only.

Commit

Permalink
Merge 9f5345e into 892d86f
Browse files Browse the repository at this point in the history
  • Loading branch information
tkuminecz committed Dec 20, 2016
2 parents 892d86f + 9f5345e commit 99446af
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 107 deletions.
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "phantasy",
"version": "0.10.4",
"version": "0.10.5",
"description": "",
"author": "Tim Kuminecz <tkuminecz@gmail.com>",
"license": "MIT",
Expand Down Expand Up @@ -54,10 +54,9 @@
"npm-run-all": "^3.1.2",
"nyc": "^10.0.0",
"tap-diff": "^0.1.1",
"tape": "^4.6.2"
"tape": "4.5.1"
},
"dependencies": {
"bluebird": "^3.4.6",
"tape": "4.5.1"
"bluebird": "^3.4.6"
}
}
4 changes: 3 additions & 1 deletion src/eff.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { Task } from './task';
/**
* The `Eff` monad
*
* Represents synchronous computations which are both dependency-injected and explicitly effectful
* Represents synchronous computations which are both dependency-injected and
* explicitly effectful. `Eff` can be used for effectful code where the effect
* resolvers are injected. This allows the effect handling to be customizable.
*/
export class Eff<E: {}, A> {

Expand Down
41 changes: 28 additions & 13 deletions src/identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,80 @@
import { inspect } from 'util';

/**
* The Identity monad
* The `Identity` monad
*
* Simply acts a container for values without any additional behavior
*
* @example let a = Identity.of(42)
*/
export class Identity<A> {

data: A

/**
* Constructs a new Maybe instance
*
* @private
*/
constructor(data: A): void {
this.data = data;
}

/**
* map :: Identity a ~> (a -> b) -> Identity b
* `map :: Identity a ~> (a -> b) -> Identity b`
*
* Transforms the value in the `Identity` instance
*/
map<B>(f: (a: A) => B): Identity<B> {
return Identity.of(f(this.data));
map<B>(transform: (a: A) => B): Identity<B> {
return Identity.of(transform(this.data));
}

/**
* andThen :: Identity a ~> (a -> Identity b) -> Identity b
* `andThen :: Identity a ~> (a -> Identity b) -> Identity b`
*
* Chains the value of the `Identity` instance
* with another `Identity`-producing function
*/
andThen<B>(next: (a: A) => Identity<B>): Identity<B> {
return next(this.data);
}

/**
* toString :: Identity a ~> () -> String
* `toString :: Identity a ~> () -> String`
*
* Returns a string representation of the `Identity` instance
*/
toString(): string {
return `Identity ${ inspect(this.data) }`;
}

/**
* of :: a -> Identity a
* `of :: a -> Identity a`
*
* Returns an `Identity` instance wrapping the giving value
*/
static of(a: A): Identity<A> {
return new Identity(a);
}

/**
* lift :: (a -> b) -> Identity a -> Identity b
* `lift :: (a -> b) -> Identity a -> Identity b`
*/
static lift<A, B>(f: (a: A) => B): * {
static lift<A, B>(f: (a: A) => B): (a: Identity<A>) => Identity<B> {
return (ma) => ma.andThen(a => Identity.of(f(a)));
}

/**
* lift2 :: (a -> b -> c) -> Identity a -> Identity b -> Identity c
* `lift2 :: (a -> b -> c) -> (Identity a -> Identity b) -> Identity c`
*/
static lift2<A, B, C>(f: (a: A, b: B) => C): * {
static lift2<A, B, C>(f: (a: A, b: B) => C): (a: Identity<A>, b: Identity<B>) => Identity<C> {
return (ma, mb) => ma.andThen(a => mb.andThen(b => Identity.of(f(a, b))));
}

/**
* lift3 :: (a -> b -> c -> d) -> Identity a -> Identity b -> Identity c -> Identity d
* `lift3 :: (a -> b -> c -> d) -> (Identity a -> Identity b -> Identity c) -> Identity d`
*/
static lift3<A, B, C, D>(f: (a: A, b: B, c: C) => D): * {
static lift3<A, B, C, D>(f: (a: A, b: B, c: C) => D): (a: Identity<A>, b: Identity<B>, c: Identity<C>) => Identity<D> {
return (ma: Identity<A>, mb: Identity<B>, mc: Identity<C>) => ma.andThen(a => mb.andThen(b => mc.andThen(c => Identity.of(f(a, b, c)))));
}

Expand Down
50 changes: 41 additions & 9 deletions src/io.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,75 @@
/**
* The `IO` monad
*
* Represents a potentially-effectful synchronous computation
* Represents a potentially-effectful synchronous computation.
*/
export class IO<A> {

runIO: () => A
_runIO: () => A

/**
* Constructs a new IO instance
* Constructs a new IO instance.
*
* @private
*/
constructor(runIO: () => A): void {
this.runIO = runIO;
this._runIO = runIO;
}

/**
* `runIO :: IO a ~> () -> a`
*
* Executes the function contained in the `IO` instance and returns the produced value. Note that any side-effects in the `IO` would be performed at this time.
*
* @example let a = IO.of(42);
*
* a.runIO() === 42;
*/
runIO(): A {
return this._runIO();
}

/**
* `map :: IO a ~> (a -> b) -> IO b`
*
* Transforms the result of the `IO` instance
* Transforms the result of the `IO` instance.
*
* @example let a = IO.of(2);
*
* a.map(a => a * 2) === IO.of(4);
*/
map<B>(transform: (a: A) => B): IO<B> {
return IO.of(transform(this.runIO()));
map<B>(f: (a: A) => B): IO<B> {
return IO.of(f(this.runIO()));
}

/**
* `andThen :: IO a ~> (a -> IO b) -> IO b`
*
* Chains the result of the `IO` instance with another `IO`-producing function
* Chains the result of the `IO` instance with another `IO`-producing function.
*
* @example let a = IO.of(42);
*
* a.andThen(a => IO.of(a / 2)) == IO.of(24);
*/
andThen<B>(next: (a: A) => IO<B>): IO<B> {
return next(this.runIO());
}

/**
* `from :: (() -> a) -> IO a`
*
* Returns a `IO` instance wrapping the given function.
*/
static from<A>(f: () => A): IO<A> {
return new IO(f);
}

/**
* `of :: a -> IO a`
*
* Returns an `IO` instance that always produces the given value
* Returns an `IO` instance that always produces the given value.
*
* @example let a = IO.of(42);
*/
static of(value: A): IO<A> {
return new IO(() => value);
Expand Down
40 changes: 25 additions & 15 deletions src/maybe.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,19 @@ type MaybeData<A> = Just<A> | Nothing
/**
* The `Maybe` monad
*
* Represents the possibility of a value or nothing. Commonly used
* to safely deal with nullable values because it is composable and
* forces the developer to explicitly handle the null case.
* Represents the possibility of the presence or absence of a value.
* Commonly used to safely deal with nullable values because it is
* composable and forces the developer to explicitly handle the null case.
*
* @example // construct a Maybe with a value
* let some = Maybe.Just(42);
*
* // construct a maybe absent of a value
* let none = Maybe.Nothing;
*
* // create Maybes from nullable values
* let ma = Maybe.of('foo'),
* mb = Maybe.of(null);
*/
export class Maybe<A> {

Expand Down Expand Up @@ -65,7 +75,7 @@ export class Maybe<A> {
/**
* `isJust :: Maybe a ~> () -> Bool`
*
* Returns `true` if the instance contains a value
* Returns `true` if the `Maybe` instance contains a value
*/
isJust(): bool {
return this.cases({
Expand All @@ -77,7 +87,7 @@ export class Maybe<A> {
/**
* `isNothing :: Maybe a ~> () -> Bool`
*
* Returns `true` if the instance is absent of value
* Returns `true` if the `Maybe` instance is absent of value
*/
isNothing(): bool {
return !this.isJust();
Expand All @@ -102,9 +112,9 @@ export class Maybe<A> {
* If `Just`, returns `Just` the value transformed by
* the given function, otherwise returns `Nothing`
*/
map<B>(transform: (a: A) => B): Maybe<B> {
map<B>(f: (a: A) => B): Maybe<B> {
return (this.data instanceof Just)
? Maybe.Just(transform(this.data.value))
? Maybe.Just(f(this.data.value))
: Maybe.Nothing;
}

Expand Down Expand Up @@ -135,7 +145,7 @@ export class Maybe<A> {
/**
* `of :: a -> Maybe a`
*
* Takes a nullable value and constructs a new `Maybe` instance containing it
* Returns `Maybe.Just` of the given value if it is not null and `Maybe.Nothing` otherwise
*/
static of(a: ?A): Maybe<A> {
return (a == null)
Expand All @@ -146,7 +156,7 @@ export class Maybe<A> {
/**
* `Just :: a -> Maybe a`
*
* Data constructor for indicating the presence of a value
* Returns a a `Maybe` instance containing the given value
*/
static Just<A>(a: A): Maybe<A> {
return new Maybe(new Just(a));
Expand All @@ -155,7 +165,7 @@ export class Maybe<A> {
/**
* `Nothing :: Maybe a`
*
* Data constructor for indicating the absence of a value
* A `Maybe` instance which is absent of a value
*/
static Nothing = (new Maybe(new Nothing()): any);

Expand All @@ -180,25 +190,25 @@ export class Maybe<A> {
*
* Takes an unary function and returns an equivalent unary function which operates on `Maybe` values
*/
static lift<A, B>(f: (a: A) => B): * {
static lift<A, B>(f: (a: A) => B): (a: Maybe<A>) => Maybe<B> {
return (ma) => ma.andThen(a => Maybe.of(f(a)));
}

/**
* `lift2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c`
* `lift2 :: (a -> b -> c) -> (Maybe a -> Maybe b) -> Maybe c`
*
* Takes an binary function and returns an equivalent binary function which operates on `Maybe` values
*/
static lift2<A, B, C>(f: (a: A, b: B) => C): * {
static lift2<A, B, C>(f: (a: A, b: B) => C): (a: Maybe<A>, b: Maybe<B>) => Maybe<C> {
return (ma, mb) => ma.andThen(a => mb.andThen(b => Maybe.of(f(a, b))));
}

/**
* `lift3 :: (a -> b -> c -> d) -> Maybe a -> Maybe b -> Maybe c -> Maybe d`
* `lift3 :: (a -> b -> c -> d) -> (Maybe a -> Maybe b -> Maybe c) -> Maybe d`
*
* Takes an ternary function and returns an equivalent ternary function which operates on `Maybe` values
*/
static lift3<A, B, C, D>(f: (a: A, b: B, c: C) => D): * {
static lift3<A, B, C, D>(f: (a: A, b: B, c: C) => D): (a: Maybe<A>, b: Maybe<B>, c: Maybe<C>) => Maybe<D> {
return (ma, mb, mc) => ma.andThen(a => mb.andThen(b => mc.andThen(c => Maybe.of(f(a, b, c)))));
}

Expand Down

0 comments on commit 99446af

Please sign in to comment.