diff --git a/CHANGELOG.md b/CHANGELOG.md index b32fd9a51..256c2b89e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ **Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a high state of flux, you're at risk of it changing without notice. +# 0.2 + +- **Breaking Change** + - complete refactoring: new technique to get higher kinded types and typeclasses + # 0.1.1 - **New Feature** diff --git a/README.md b/README.md index 585d12a5e..75f8145d1 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ A mix of - PureScript - Scala -The idea (faking higher kinded types in TypeScript) is based on the paper [Lightweight higher-kinded polymorphism](https://www.cl.cam.ac.uk/~jdy22/papers/lightweight-higher-kinded-polymorphism.pdf), [elm-brands](https://github.com/joneshf/elm-brands) and [flow-static-land](https://github.com/gcanti/flow-static-land). - See the section "Technical overview" below for an explanation of the technique. # Algebraic types @@ -53,331 +51,69 @@ See the section "Technical overview" below for an explanation of the technique. # Technical overview -## A basic `Option` type - -File: Option.ts - -```ts -// definition -type None = { - __tag: 'None' -} - -type Some = { - __tag: 'Some', - value: A -} - -type Option = None | Some - -// helpers -const none: None = { __tag: 'None' } - -function some(a: A): Option { - return { __tag: 'Some', value: a } -} - -// a specialised map for Option -function map(f: (a: A) => B, fa: Option): Option { - switch (fa.__tag) { - case 'None' : - return fa - case 'Some' : - return some(f(fa.value)) - } -} -``` - -Usage - -```ts -const double = (n: number): number => n * 2 -const length = (s: string): number => s.length - -console.log(map(double, some(1))) // { __tag: 'Some', value: 2 } -console.log(map(double, none)) // { __tag: 'None' } -console.log(map(length, some(2))) // <= error -``` - -## Adding static land support - -TypeScript doesn't support higher kinded types - -```ts -interface StaticFunctor { - map(f: (a: A) => B, fa: ?): ? -} -``` - -but we can fake them with an interface - -```ts -interface HKT { - __hkt: F - __hkta: A -} -``` - -where `F` is a unique identifier representing the type constructor and `A` its type parameter. - -Now we can define a generic `StaticFunctor` interface - -```ts -interface StaticFunctor { - map(f: (a: A) => B, fa: HKT): HKT -} -``` - -and a new `Option` type - -```ts -// unique identifier -type URI = 'Option' - -type None = { - __tag: 'None' - __hkt: URI - __hkta: any -} - -type Some = { - __tag: 'Some', - __hkt: URI - __hkta: A - value: A -} - -type Option = None | Some - -const none: None = { - __tag: 'None', - __hkt: 'Option', - __hkta: undefined as any -} - -function some(a: A): Option { - return { - __tag: 'Some', - __hkt: 'Option', - __hkta: a, - value: a - } -} - -function map(f: (a: A) => B, fa: Option): Option { - switch (fa.__tag) { - case 'None' : - return fa - case 'Some' : - return some(f(fa.value)) - } -} -``` - -Let's check the implementation - -```ts -// if this type-checks the signature is likely correct -;({ map } as StaticFunctor) -``` - -Usage - -```ts -console.log(map(double, some(1))) // { __tag: 'Some', __hkt: 'Option', __hkta: 2, value: 2 } -console.log(map(double, none)) // { __tag: 'None', __hkt: 'Option', __hkta: undefined } -console.log(map(length, some(2))) // <= error -``` - -Exports can be directly used as a static land dictionary - -```ts -import * as option from './Option' // option contains map -``` - -There's a problem though. Let's define a generic `lift` function based on the `StaticFunctor` interface - -```ts -class FunctorOps { - lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT) => HKT { - return fa => functor.map(f, fa) - } -} - -const ops = new FunctorOps() -``` - -If we try to use `lift` and `map` together TypeScript raises an error - -```ts -const maybeLength = ops.lift({ map }, length) - -map(double, maybeLength(some('hello'))) -/* -Argument of type 'HKT<"Option", number>' is not assignable to parameter of type 'Option'. - Type 'HKT<"Option", number>' is not assignable to type 'Some'. - Property '__tag' is missing in type 'HKT<"Option", number>' -*/ -``` - -Every `Option` is a `HKT<"Option", A>` but the converse is not true. In order to fix this (we **know** that `Option = HKT<"Option", A>`) functions like `map` should accept the more general version `HKT<"Option", A>` and return the more specific version `Option` - -```ts -type HKTOption = HKT - -function map(f: (a: A) => B, fa: HKTOption): Option { - const option = fa as Option - switch (option.__tag) { - case 'None' : - return option - case 'Some' : - return some(f(option.value)) - } -} +## Higher kinded types and type classes -map(double, maybeLength(some('hello'))) // ok -``` +Higher kinded types are represented by a unique string literal (called `URI`). -We can do even better. Note that `maybeLength` has the following signature +There's a central type dictionary where a mapping `URI` -> concrete type is stored ```ts -(fa: HKT<"Option", string>) => HKT<"Option", number> +// file ./HTK.ts +export interface HKT {} ``` -We'd like to have `(fa: Option) => Option` instead. - -We'll use a feature called [Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) for that - -Let's move the `Functor` definition to its own file - -File: Functor.ts - -```ts -export interface StaticFunctor { - map(f: (a: A) => B, fa: HKT): HKT -} - -export class FunctorOps { - // base signature - lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT) => HKT - lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT) => HKT { - return fa => functor.map(f, fa) - } -} - -export const ops = new FunctorOps() -``` +Instances can be defined (everywhere) using a feature called [Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) so there's no danger of name conflict (the typechecker checks for duplicates). -File: Option.ts +Here's a mapping between the string literal `'Option'` and the concrete type `Option` ```ts -declare module './Functor' { - interface FunctorOps { - // specialized signature for Option - lift(functor: StaticFunctor, f: (a: A) => B): (fa: Option) => Option +// file ./Option.ts +declare module './HKT' { + interface HKT { + Option: Option } } -``` - -That means that `lift` is truly polimophic and may have a specialized signature for any higher kinded type. - -## Adding fantasy land support - -We can define a generic `FantasyFunctor` interface - -```ts -interface FantasyFunctor extends HKT { - map(f: (a: A) => B): FantasyFunctor -} -``` -And now let's change the implementation of `None` and `Some` +export const URI = 'Option' +export type URI = typeof URI +export type Option = None | Some -```ts -type URI = 'Option' - -class None implements FantasyFunctor { - __tag: 'None' - __hkt: URI - __hkta: any +export class None { + readonly _URI: URI map(f: (a: A) => B): Option { return none } + ... } -class Some implements FantasyFunctor { - __tag: 'Some' - __hkt: URI - __hkta: A - constructor(public value: A) { } +export Some { + readonly _URI: URI + // fantasy-land implementation map(f: (a: A) => B): Option { - return some(f(this.value)) + return new Some(f(this.value)) } + ... } -type Option = None | Some - -const none = new None() - -function some(a: A): Option { - return new Some(a) -} -``` - -Note that `None` has a type parameter, because the signature of `map` (the method) must be the same for both `None` and `Some` otherwise TypeScript will complain. - -The implementation of `map` (the static function) is now trivial. - -```ts -function map(f: (a: A) => B, fa: HKTOption): Option { - return (fa as Option).map(f) +// static-land implementation +export function map(f: (a: A) => B, fa: Option): Option { + return fa.map(f) } ``` -## Faking Haskell's type classes - -Let's add to `FunctorOps` a polimorphic `map` function - -```ts -export class FunctorOps { - lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT) => HKT - lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT) => HKT { - return fa => functor.map(f, fa) - } +Concrete types can be retrieved by their `URI` using a feature called [Index types](https://www.typescriptlang.org/docs/handbook/advanced-types.html). - map(f: (a: A) => B, fa: FantasyFunctor): FantasyFunctor - map(f: (a: A) => B, fa: FantasyFunctor): FantasyFunctor { - return fa.map(f) - } -} -``` +Type classes are implemented following (when possible) both the [static-land](https://github.com/rpominov/static-land) spec and the [fantasy-land](https://github.com/fantasyland/fantasy-land) spec. -And the corresponding module augmentation in the `Option.ts` file + Here's the definition of the type class `Functor` (following the static-land spec) ```ts -declare module './Functor' { - interface FunctorOps { - lift(functor: StaticFunctor, f: (a: A) => B): (fa: Option) => Option - map(f: (a: A) => B, fa: HKTOption): Option - } +export interface StaticFunctor { + URI: F + map(f: (a: A) => B, fa: HKT[F]): HKT[F] } ``` -> Roughly speaking the `map` definition in `FunctorOps` corresponds to a type class definition, while the module augmentation part corresponds to declaring an instance - -Now `map` is a truly polimorphic function with prefect type inference - -```ts -import * as option from 'fp-ts/lib/Option' -import * as io from 'fp-ts/lib/IO' -const length = (s: string): number => s.length - -// x :: Option -const x = map(length, option.of('hello')) -// y :: IO -const y = map(length, io.of('hello')) -``` - # License The MIT License (MIT) diff --git a/package.json b/package.json index b0252fc69..32ef577aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fp-ts", - "version": "0.1.1", + "version": "0.2.0", "description": "Functional programming in TypeScript", "files": [ "lib", @@ -11,9 +11,11 @@ "typings": "lib/index.d.ts", "scripts": { "lint": "tslint src/**/*.ts test/**/*.ts", + "typings-checker": "typings-checker --allow-expect-error --project typings-checker/tsconfig.json typings-checker/index.ts", "mocha": "mocha -r ts-node/register test/*.ts", - "test": "npm run lint && npm run mocha", - "build": "rm -rf lib/* && tsc && tsc -m es6 --outDir lib-jsnext" + "test": "npm run lint && npm run typings-checker && npm run mocha", + "clean": "rm -rf lib/* && rm -rf lib-jsnext/*", + "build": "npm run clean && tsc && tsc -m es6 --outDir lib-jsnext" }, "repository": { "type": "git", @@ -27,13 +29,14 @@ "homepage": "https://github.com/gcanti/fp-ts", "dependencies": {}, "devDependencies": { - "@types/mocha": "^2.2.38", - "@types/node": "^7.0.4", - "mocha": "^3.2.0", - "ts-node": "^2.0.0", - "tslint": "^4.4.2", - "tslint-config-standard": "^4.0.0", - "typescript": "^2.2.0" + "@types/mocha": "2.2.38", + "@types/node": "7.0.4", + "mocha": "3.2.0", + "ts-node": "2.0.0", + "tslint": "4.4.2", + "tslint-config-standard": "4.0.0", + "typescript": "2.2.2", + "typings-checker": "1.1.2" }, "tags": [ "typescript", diff --git a/src/Alt.ts b/src/Alt.ts index f5fe972a0..bd525801a 100644 --- a/src/Alt.ts +++ b/src/Alt.ts @@ -1,21 +1,10 @@ -import { HKT } from './HKT' -import { StaticFunctor } from './Functor' -import { Function1 } from './function' +import { HKT, HKTS } from './HKT' +import { StaticFunctor, FantasyFunctor } from './Functor' -export interface StaticAlt extends StaticFunctor { - alt(fx: HKT, fy: HKT): HKT +export interface StaticAlt extends StaticFunctor { + alt(fx: HKT[F], fy: HKT[F]): HKT[F] } -export interface FantasyAlt { - map(f: Function1): FantasyAlt - alt(fy: FantasyAlt): FantasyAlt +export interface FantasyAlt extends FantasyFunctor { + alt(fy: FantasyAlt): HKT[F] } - -export class AltOps { - alt(fx: FantasyAlt, fy: FantasyAlt): FantasyAlt - alt(fx: FantasyAlt, fy: FantasyAlt): FantasyAlt { - return fx.alt(fy) - } -} - -export const ops = new AltOps() diff --git a/src/Alternative.ts b/src/Alternative.ts index 26e682834..1ea13c32d 100644 --- a/src/Alternative.ts +++ b/src/Alternative.ts @@ -1,13 +1,7 @@ -import { StaticApplicative } from './Applicative' -import { StaticPlus } from './Plus' -import { Function1 } from './function' +import { HKTS } from './HKT' +import { StaticApplicative, FantasyApplicative } from './Applicative' +import { StaticPlus, FantasyPlus } from './Plus' -export interface StaticAlternative extends StaticApplicative, StaticPlus {} +export interface StaticAlternative extends StaticApplicative, StaticPlus {} -export interface FantasyAlternative { - map(f: Function1): FantasyAlternative - of(b: B): FantasyAlternative - ap(fab: FantasyAlternative>): FantasyAlternative - alt(fy: FantasyAlternative): FantasyAlternative - zero(): FantasyAlternative -} +export interface FantasyAlternative extends FantasyApplicative, FantasyPlus {} diff --git a/src/Applicative.ts b/src/Applicative.ts index 185e7bdad..fe43f0ad3 100644 --- a/src/Applicative.ts +++ b/src/Applicative.ts @@ -1,11 +1,7 @@ -import { StaticPointedFunctor } from './PointedFunctor' -import { StaticApply } from './Apply' -import { Function1 } from './function' +import { HKTS } from './HKT' +import { StaticPointed, FantasyPointed } from './Pointed' +import { StaticApply, FantasyApply } from './Apply' -export interface StaticApplicative extends StaticPointedFunctor, StaticApply {} +export interface StaticApplicative extends StaticPointed, StaticApply {} -export interface FantasyApplicative { - map(f: Function1): FantasyApplicative - of(b: B): FantasyApplicative - ap(fab: FantasyApplicative>): FantasyApplicative -} +export interface FantasyApplicative extends FantasyPointed, FantasyApply {} diff --git a/src/Apply.ts b/src/Apply.ts index 1debcdd82..d836d1a35 100644 --- a/src/Apply.ts +++ b/src/Apply.ts @@ -1,36 +1,29 @@ -import { HKT } from './HKT' -import { StaticFunctor } from './Functor' -import { Function1, Function2, Function3, Function4, Curried2, Curried3, Curried4 } from './function' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' +import { StaticFunctor, FantasyFunctor } from './Functor' +import { Curried2, Curried3, Curried4 } from './function' -export interface StaticApply extends StaticFunctor { - ap(fab: HKT>, fa: HKT): HKT +export interface StaticApply extends StaticFunctor { + ap(fab: HKT<(a: A) => B>[F], fa: HKT[F]): HKT[F] } -export interface FantasyApply { - map(f: Function1): FantasyApply - ap(fab: FantasyApply>): FantasyApply +export interface FantasyApply extends FantasyFunctor { + ap(fab: FantasyApply B>): HKT[F] } -export class ApplyOps { - ap(fab: FantasyApply>, fa: FantasyApply): FantasyApply - ap(fab: FantasyApply>, fa: FantasyApply): FantasyApply { - return fa.ap(fab) - } - - liftA2(apply: StaticApply, f: Curried2): Function2, HKT, HKT> - liftA2(apply: StaticApply, f: Curried2): Function2, HKT, HKT> { - return (fa, fb) => apply.ap(apply.map(f, fa), fb) - } - - liftA3(apply: StaticApply, f: Curried3): Function3, HKT, HKT, HKT> - liftA3(apply: StaticApply, f: Curried3): Function3, HKT, HKT, HKT> { - return (fa, fb, fc) => apply.ap(apply.ap(apply.map(f, fa), fb), fc) - } +export function liftA2(apply: StaticApply, f: Curried2): (fa: HKT2[F], fb: HKT2[F]) => HKT2[F] +export function liftA2(apply: StaticApply, f: Curried2): (fa: HKT[F], fb: HKT[F]) => HKT[F] +export function liftA2(apply: StaticApply, f: Curried2): (fa: HKT[F], fb: HKT[F]) => HKT[F] { + return (fa, fb) => apply.ap(apply.map(f, fa), fb) +} - liftA4(apply: StaticApply, f: Curried4): Function4, HKT, HKT, HKT, HKT> - liftA4(apply: StaticApply, f: Curried4): Function4, HKT, HKT, HKT, HKT> { - return (fa, fb, fc, fd) => apply.ap(apply.ap(apply.ap(apply.map(f, fa), fb), fc), fd) - } +export function liftA3(apply: StaticApply, f: Curried3): (fa: HKT2[F], fb: HKT2[F], fc: HKT2[F]) => HKT2[F] +export function liftA3(apply: StaticApply, f: Curried3): (fa: HKT[F], fb: HKT[F], fc: HKT[F]) => HKT[F] +export function liftA3(apply: StaticApply, f: Curried3): (fa: HKT[F], fb: HKT[F], fc: HKT[F]) => HKT[F] { + return (fa, fb, fc) => apply.ap(apply.ap D>(apply.map(f, fa), fb), fc) } -export const ops = new ApplyOps() +export function liftA4(apply: StaticApply, f: Curried4): (fa: HKT2[F], fb: HKT2[F], fc: HKT2[F], fd: HKT2[F]) => HKT2[F] +export function liftA4(apply: StaticApply, f: Curried4): (fa: HKT[F], fb: HKT[F], fc: HKT[F], fd: HKT[F]) => HKT[F] +export function liftA4(apply: StaticApply, f: Curried4): (fa: HKT[F], fb: HKT[F], fc: HKT[F], fd: HKT[F]) => HKT[F] { + return (fa, fb, fc, fd) => apply.ap(apply.ap E>(apply.ap>(apply.map(f, fa), fb), fc), fd) +} diff --git a/src/Array.ts b/src/Array.ts index da6c7c454..8fb65704b 100644 --- a/src/Array.ts +++ b/src/Array.ts @@ -1,36 +1,34 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { StaticMonoid } from './Monoid' import { StaticApplicative } from './Applicative' -import { FantasyFunctor } from './Functor' import { StaticMonad } from './Monad' import { StaticFoldable } from './Foldable' import { StaticTraversable } from './Traversable' import { StaticAlternative } from './Alternative' import { StaticPlus } from './Plus' -import { ops } from './Apply' -import { HKTOption, Option } from './Option' +import { liftA2 } from './Apply' +import { Option } from './Option' import * as option from './Option' import { StaticOrd, toNativeComparator } from './Ord' -import { Predicate, identity, constant, curry, Lazy, Function1, Function2, Endomorphism } from './function' +import { Predicate, identity, constant, curry, Lazy, Endomorphism } from './function' -export type URI = 'Array' - -export type HKTArray = HKT - -declare global { - interface Array extends FantasyFunctor { - _hkt: URI - _hkta: T +declare module './HKT' { + interface HKT { + Array: Array } } +export const URI = 'Array' + +export type URI = typeof URI + export const empty: Lazy> = constant([]) export function concat(x: Array, y: Array): Array { return x.concat(y) } -export function map(f: Function1, fa: Array): Array { +export function map(f: (a: A) => B, fa: Array): Array { return fa.map(f) } @@ -38,30 +36,34 @@ export function of(a: A): Array { return [a] } -export function ap(fab: Array>, fa: Array): Array { +export function ap(fab: Array<(a: A) => B>, fa: Array): Array { return fab.reduce((acc: Array, f) => acc.concat(fa.map(f)), []) } -export function chain(f: Function1>, fa: Array): Array { +export function chain(f: (a: A) => Array, fa: Array): Array { return fa.reduce((acc: Array, a) => acc.concat(f(a)), []) } -export function reduce(f: Function2, b: B, fa: Array): B { +export function reduce(f: (b: B, a: A) => B, b: B, fa: Array): B { return fa.reduce(f, b) } export const curriedSnoc = curry(snoc) -export function traverse(applicative: StaticApplicative, f: Function1>, ta: Array): HKT> { - const snocA2 = ops.liftA2(applicative, curriedSnoc) - return reduce((fab, a) => snocA2(fab, f(a)), applicative.of(empty()), ta) +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F], ta: Array) => HKT2>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Array) => HKT>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Array) => HKT>[F] { + return (f: (a: A) => HKT[F], ta: Array) => { + const snocA2 = liftA2(applicative, curriedSnoc) + return reduce((fab, a) => snocA2(fab, f(a)), applicative.of(empty()), ta) + } } export const zero = empty export const alt = concat -export function unfoldr(f: Function1>, b: B): Array { +export function unfoldr(f: (b: B) => Option<[A, B]>, b: B): Array { const ret: Array = [] let bb = b while (true) { @@ -199,42 +201,18 @@ export function reverse(as: Array): Array { return copy(as).reverse() } -export function mapOption(f: Function1>, as: Array): Array { +export function mapOption(f: (a: A) => Option, as: Array): Array { return chain(a => option.fold(empty, of, f(a)), as) } -export function catOptions(as: Array>): Array { - return mapOption, A>(identity, as) +export function catOptions(as: Array>): Array { + return mapOption, A>(identity, as) } export function sort(ord: StaticOrd, as: Array): Array { return copy(as).sort(toNativeComparator(ord.compare)) } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: HKTArray): Array - lift(functor: StaticFunctor, f: Function1): Function1, Array> - } -} - -declare module './Foldable' { - interface FoldableOps { - reduce(f: Function2, b: B, fa: HKTArray): B - foldMap(monoid: StaticMonoid, f: Function1, fa: HKTArray): M - foldMapS(foldable: StaticFoldable, monoid: StaticMonoid, f: Function1, fa: HKTArray): M - } -} - -declare module './Traversable' { - interface TraversableOps { - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable, - tfa: HKTArray>): HKT> - } -} - // tslint:disable-next-line no-unused-expression ;( { empty, concat, map, of, ap, chain, reduce, traverse, zero, alt } as ( diff --git a/src/Bifunctor.ts b/src/Bifunctor.ts index 09cecf328..10dc65805 100644 --- a/src/Bifunctor.ts +++ b/src/Bifunctor.ts @@ -1,19 +1,10 @@ import { HKT2 } from './HKT' -import { Function1 } from './function' -export interface StaticBifunctor { - bimap(f: Function1, g: Function1, fac: HKT2): HKT2 +export interface StaticBifunctor> { + readonly URI: F + bimap(f: (a: A) => B, g: (c: C) => D, fac: HKT2[F]): HKT2[F] } -export interface FantasyBifunctor extends HKT2 { - bimap(f: Function1, g: Function1): FantasyBifunctor +export interface FantasyBifunctor, A, C> { + bimap(f: (a: A) => B, g: (c: C) => D): HKT2[F] } - -export class BifunctorOps { - bimap(f: Function1, g: Function1, fac: FantasyBifunctor): FantasyBifunctor - bimap(f: Function1, g: Function1, fac: FantasyBifunctor): FantasyBifunctor { - return fac.bimap(f, g) - } -} - -export const ops = new BifunctorOps() diff --git a/src/Chain.ts b/src/Chain.ts index 1e9f42e96..acbb9b3e2 100644 --- a/src/Chain.ts +++ b/src/Chain.ts @@ -1,28 +1,17 @@ -import { HKT } from './HKT' -import { StaticApply } from './Apply' -import { Kleisli, Function1, identity } from './function' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' +import { StaticApply, FantasyApply } from './Apply' +import { Kleisli, identity } from './function' -export interface StaticChain extends StaticApply { - chain(f: Kleisli, fa: HKT): HKT +export interface StaticChain extends StaticApply { + chain(f: Kleisli, fa: HKT[F]): HKT[F] } -export interface FantasyChain { - map(f: Function1): FantasyChain - of(b: B): FantasyChain - ap(fab: FantasyChain>): FantasyChain - chain(f: Kleisli): FantasyChain +export interface FantasyChain extends FantasyApply { + chain(f: Kleisli): HKT[F] } -export class ChainOps { - chain(f: Kleisli, fa: FantasyChain): FantasyChain - chain(f: Kleisli, fa: FantasyChain): FantasyChain { - return fa.chain(f) - } - - flatten(mma: FantasyChain>): FantasyChain - flatten(mma: FantasyChain>): FantasyChain { - return mma.chain(identity) - } +export function flatten(chain: StaticChain): (mma: HKT2[F]>[F]) => HKT2[F] +export function flatten(chain: StaticChain): (mma: HKT[F]>[F]) => HKT[F] +export function flatten(chain: StaticChain): (mma: HKT[F]>[F]) => HKT[F] { + return (mma: HKT[F]>[F]) => chain.chain[F], A>(identity, mma) } - -export const ops = new ChainOps() diff --git a/src/ChainRec.ts b/src/ChainRec.ts index 02f7e1ed7..f2e4b5910 100644 --- a/src/ChainRec.ts +++ b/src/ChainRec.ts @@ -1,14 +1,17 @@ -import { HKT } from './HKT' -import { StaticChain } from './Chain' +import { HKT, HKTS } from './HKT' +import { StaticChain, FantasyChain } from './Chain' import { Either, Right } from './Either' import { isLeft } from './Either' -import { Function1 } from './function' -export interface StaticChainRec extends StaticChain { - chainRec(f: Function1>>, a: A): HKT +export interface StaticChainRec extends StaticChain { + chainRec(f: (a: A) => HKT>[F], a: A): HKT[F] } -export function tailRec(f: Function1>, a: A): B { +export interface FantasyChainRec extends FantasyChain { + chainRec(f: (a: A) => HKT>[F]): HKT[F] +} + +export function tailRec(f: (a: A) => Either, a: A): B { let v = f(a) while (isLeft(v)) { v = f(v.value) diff --git a/src/Comonad.ts b/src/Comonad.ts index ac5fb250b..d5c51b2a7 100644 --- a/src/Comonad.ts +++ b/src/Comonad.ts @@ -1,11 +1,7 @@ -import { StaticExtend } from './Extend' -import { StaticCopointed } from './Copointed' -import { Cokleisli, Function1 } from './function' +import { HKTS } from './HKT' +import { StaticExtend, FantasyExtend } from './Extend' +import { StaticCopointed, FantasyCopointed } from './Copointed' -export interface StaticComonad extends StaticExtend, StaticCopointed {} +export interface StaticComonad extends StaticExtend, StaticCopointed {} -export interface FantasyComonad { - map(f: Function1): FantasyComonad - extract(): A - extend(f: Cokleisli): FantasyComonad -} +export interface FantasyComonad extends FantasyExtend, FantasyCopointed {} diff --git a/src/Const.ts b/src/Const.ts index 184104435..8061a2dc6 100644 --- a/src/Const.ts +++ b/src/Const.ts @@ -1,4 +1,3 @@ -import { HKT } from './HKT' import { StaticMonoid } from './Monoid' import { StaticFunctor, FantasyFunctor } from './Functor' import { StaticContravariant, FantasyContravariant } from './Contravariant' @@ -6,28 +5,34 @@ import { StaticApplicative } from './Applicative' import { StaticApply } from './Apply' import { StaticSemigroup } from './Semigroup' import { StaticSetoid } from './Setoid' -import { identity, Function1 } from './function' +import { identity } from './function' -export type URI = 'Const' +declare module './HKT' { + interface HKT { + Const: Const + } + interface HKT2 { + Const: Const + } +} -export type HKTURI = HKT +export const URI = 'Const' -export type HKTConst = HKT, A> +export type URI = typeof URI export class Const implements - FantasyFunctor, A>, - FantasyContravariant, A> { + FantasyFunctor, + FantasyContravariant { - readonly _hkt: HKTURI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: L) {} - map(f: Function1): Const { + map(f: (b: B) => C): Const { return this as any } - contramap(f: Function1): Const { + contramap(f: (c: C) => B): Const { return this as any } - fold(f: Function1): B { + fold(f: (l: L) => B): B { return f(this.value) } equals(setoid: StaticSetoid, fy: Const): boolean { @@ -35,35 +40,33 @@ export class Const implements } } -export function to(fa: HKTConst): Const { - return fa as Const +export function equals(setoid: StaticSetoid, fx: Const, fy: Const): boolean { + return fx.equals(setoid, fy) } -export function equals(setoid: StaticSetoid, fx: Const, fy: HKTConst): boolean { - return fx.equals(setoid, fy as Const) +export function map(f: (a: A) => B, fa: Const): Const { + return fa.map(f) } -export function map(f: Function1, fa: HKTConst): Const { - return (fa as Const).map(f) +export function contramap(f: (b: B) => A, fa: Const): Const { + return fa.contramap(f) } -export function contramap(f: Function1, fa: HKTConst): Const { - return (fa as Const).contramap(f) -} - -export function getApply(semigroup: StaticSemigroup): StaticApply> { +export function getApply(semigroup: StaticSemigroup): StaticApply { return { + URI, map, - ap(fab: Const>, fa: Const): Const { + ap(fab: Const B>, fa: Const): Const { return new Const(semigroup.concat(fab.fold(identity), fa.fold(identity))) } } } -export function getApplicative(monoid: StaticMonoid): StaticApplicative> { +export function getApplicative(monoid: StaticMonoid): StaticApplicative { const { ap } = getApply(monoid) const empty = new Const(monoid.empty()) return { + URI, map, ap, of(b: A): Const { @@ -72,24 +75,10 @@ export function getApplicative(monoid: StaticMonoid): StaticApplicative(f: Function1, fa: FantasyFunctor, A>): Const - lift(functor: StaticFunctor>, f: Function1): Function1, Const> - } -} - -declare module './Contravariant' { - interface ContravariantOps { - contramap(f: Function1, fa: FantasyContravariant, A>): Const - lift(functor: StaticContravariant>, f: Function1): Function1, Const> - } -} - // tslint:disable-next-line no-unused-expression ;( { map, contramap } as ( - StaticFunctor> & - StaticContravariant> + StaticFunctor & + StaticContravariant ) ) diff --git a/src/Contravariant.ts b/src/Contravariant.ts index 21e11b034..16ea224b0 100644 --- a/src/Contravariant.ts +++ b/src/Contravariant.ts @@ -1,24 +1,16 @@ -import { HKT } from './HKT' -import { Function1 } from './function' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' -export interface StaticContravariant { - contramap(f: Function1, fa: HKT): HKT +export interface StaticContravariant { + readonly URI: F + contramap(f: (b: B) => A, fa: HKT[F]): HKT[F] } -export interface FantasyContravariant extends HKT { - contramap(f: Function1): FantasyContravariant +export interface FantasyContravariant { + contramap(f: (b: B) => A): HKT[F] } -export class ContravariantOps { - contramap(f: Function1, fa: FantasyContravariant): FantasyContravariant - contramap(f: Function1, fa: FantasyContravariant): FantasyContravariant { - return fa.contramap(f) - } - - lift(contravariant: StaticContravariant, f: Function1): Function1, HKT> - lift(contravariant: StaticContravariant, f: Function1): Function1, HKT> { - return fa => contravariant.contramap(f, fa) - } +export function lift(contravariant: StaticContravariant, f: (b: B) => A): (fa: HKT2[F]) => HKT2[F] +export function lift(contravariant: StaticContravariant, f: (b: B) => A): (fa: HKT[F]) => HKT[F] +export function lift(contravariant: StaticContravariant, f: (b: B) => A): (fa: HKT[F]) => HKT[F] { + return fa => contravariant.contramap(f, fa) } - -export const ops = new ContravariantOps() diff --git a/src/Copointed.ts b/src/Copointed.ts index 7b0a0cbe4..a0a1e55ef 100644 --- a/src/Copointed.ts +++ b/src/Copointed.ts @@ -1,12 +1,8 @@ -import { HKT } from './HKT' -import { StaticFunctor } from './Functor' -import { Function1 } from './function' +import { HKT, HKTS } from './HKT' +import { StaticFunctor, FantasyFunctor } from './Functor' -export interface StaticCopointed extends StaticFunctor { - extract(ca: HKT): A +export interface StaticCopointed extends StaticFunctor { + extract(ca: HKT[F]): A } -export interface FantasyCopointed { - map(f: Function1): FantasyCopointed - extract(): A -} +export interface FantasyCopointed extends FantasyFunctor {} diff --git a/src/Either.ts b/src/Either.ts index 7f445579a..f669bb5d8 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -1,4 +1,4 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { StaticApplicative } from './Applicative' import { StaticMonad, FantasyMonad } from './Monad' import { StaticFoldable, FantasyFoldable } from './Foldable' @@ -7,66 +7,73 @@ import { StaticSetoid } from './Setoid' import { StaticTraversable, FantasyTraversable } from './Traversable' import { StaticBifunctor, FantasyBifunctor } from './Bifunctor' import { StaticAlt, FantasyAlt } from './Alt' -import { StaticMonoid } from './Monoid' import { StaticChainRec, tailRec } from './ChainRec' import { Option, none, some } from './Option' -import { constFalse, constTrue, Function1, Function2, Function3, Function4, Predicate, Curried2, Curried3, Curried4, Kleisli, Cokleisli, Lazy } from './function' +import { constFalse, constTrue, Predicate, Lazy } from './function' -export type URI = 'Either' +declare module './HKT' { + interface HKT { + Either: Either + } + interface HKT2 { + Either: Either + } +} -export type HKTURI = HKT +export const URI = 'Either' -export type HKTEither = HKT, A> +export type URI = typeof URI export type Either = Left | Right export class Left implements - FantasyMonad, A>, - FantasyFoldable, A>, - FantasyTraversable, A>, - FantasyAlt, A>, - FantasyExtend, A>, + FantasyMonad, + FantasyFoldable, + FantasyTraversable, + FantasyAlt, + FantasyExtend, FantasyBifunctor { static of = of readonly _tag: 'Left' - readonly _hkt: HKTURI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: L) {} - map(f: Function1): Either { + map(f: (a: A) => B): Either { return this as any } of(b: B): Either { return of(b) } - ap(fab: Either>): Either { + ap(fab: Either B>): Either { return this as any } - chain(f: Function1>): Either { + chain(f: (a: A) => Either): Either { return this as any } - bimap(f: Function1, g: Function1): Either { + bimap(f: (l: L) => L2, g: (a: A) => B): Either { return new Left(f(this.value)) } alt(fy: Either): Either { return fy } - extend(f: Function1, B>): Either { + extend(f: (ea: Either) => B): Either { return this as any } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return b } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.of(this as any) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.of(this as any) } - fold(left: Function1, right: Function1): B { + fold(left: (l: L) => B, right: (a: A) => B): B { return left(this.value) } equals(setoid: StaticSetoid, fy: Either): boolean { return fy.fold(constTrue, constFalse) } - mapLeft(f: Function1): Either { + mapLeft(f: (l: L) => L2): Either { return left(f(this.value)) } toOption(): Option { @@ -81,55 +88,56 @@ export class Left implements } export class Right implements - FantasyMonad, A>, - FantasyFoldable, A>, - FantasyTraversable, A>, - FantasyAlt, A>, - FantasyExtend, A>, + FantasyMonad, + FantasyFoldable, + FantasyTraversable, + FantasyAlt, + FantasyExtend, FantasyBifunctor { static of = of readonly _tag: 'Right' - readonly _hkt: HKTURI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: A) {} - map(f: Function1): Either { + map(f: (a: A) => B): Either { return new Right(f(this.value)) } of(b: B): Either { return of(b) } - ap(fab: Either>): Either { + ap(fab: Either B>): Either { if (isRight(fab)) { return this.map(fab.value) } return fab as any } - chain(f: Function1>): Either { + chain(f: (a: A) => Either): Either { return f(this.value) } - bimap(f: Function1, g: Function1): Either { + bimap(f: (l: L) => L2, g: (a: A) => B): Either { return new Right(g(this.value)) } alt(fy: Either): Either { return this } - extend(f: Function1, B>): Either { + extend(f: (ea: Either) => B): Either { return new Right(f(this)) } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return f(b, this.value) } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.map(b => of(b), f(this.value)) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.map((b: B) => of(b), f(this.value)) } - fold(left: Function1, right: Function1): B { + fold(left: (l: L) => B, right: (a: A) => B): B { return right(this.value) } equals(setoid: StaticSetoid, fy: Either): boolean { return fy.fold(constFalse, y => setoid.equals(this.value, y)) } - mapLeft(f: Function1): Either { + mapLeft(f: (l: L) => L2): Either { return this as any } toOption(): Option { @@ -143,55 +151,53 @@ export class Right implements } } -export function to(fa: HKTEither): Either { - return fa as Either -} - -export function equals(setoid: StaticSetoid, fx: HKTEither, fy: HKTEither): boolean { - return (fx as Either).equals(setoid, (fy as Either)) +export function equals(setoid: StaticSetoid, fx: Either, fy: Either): boolean { + return fx.equals(setoid, fy) } -export function fold(left: Function1, right: Function1, fa: HKTEither): B { - return (fa as Either).fold(left, right) +export function fold(left: (l: L) => B, right: (a: A) => B, fa: Either): B { + return fa.fold(left, right) } -export function map(f: Function1, fa: HKTEither): Either { - return (fa as Either).map(f) +export function map(f: (a: A) => B, fa: Either): Either { + return fa.map(f) } export function of(a: A): Right { return new Right(a) } -export function ap(fab: HKTEither>, fa: HKTEither): Either { - return (fa as Either).ap(fab as Either>) +export function ap(fab: Either B>, fa: Either): Either { + return fa.ap(fab) } -export function chain(f: Function1>, fa: HKTEither): Either { - return (fa as Either).chain(f as Function1>) +export function chain(f: (a: A) => Either, fa: Either): Either { + return fa.chain(f) } -export function bimap(f: Function1, g: Function1, fa: HKTEither): Either { - return (fa as Either).bimap(f, g) +export function bimap(f: (l: L) => L2, g: (a: A) => B, fa: Either): Either { + return fa.bimap(f, g) } -export function alt(fx: HKTEither, fy: HKTEither): Either { - return (fx as Either).alt(fy as Either) +export function alt(fx: Either, fy: Either): Either { + return fx.alt(fy) } -export function extend(f: Function1, B>, ea: HKTEither): Either { - return (ea as Either).extend(f) +export function extend(f: (ea: Either) => B, ea: Either): Either { + return ea.extend(f) } -export function reduce(f: Function2, b: B, fa: HKTEither): B { - return (fa as Either).reduce(f, b) +export function reduce(f: (b: B, a: A) => B, b: B, fa: Either): B { + return fa.reduce(f, b) } -export function traverse(applicative: StaticApplicative, f: Function1>, ta: HKTEither): HKT> { - return (ta as Either).traverse(applicative, f) +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F], ta: Either) => HKT2>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Either) => HKT>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Either) => HKT>[F] { + return (f: (a: A) => HKT[F], ta: Either) => ta.traverse(applicative)(f) } -export function chainRec(f: Function1>>, a: A): Either { +export function chainRec(f: (a: A) => Either>, a: A): Either { return tailRec((e: Either>) => e.fold( (l: L) => right(left(l)), (r: Either) => r.fold( @@ -201,11 +207,11 @@ export function chainRec(f: Function1>>, a: A ), f(a)) } -export function isLeft(fa: HKTEither): fa is Left { +export function isLeft(fa: Either): fa is Left { return fa instanceof Left } -export function isRight(fa: HKTEither): fa is Right { +export function isRight(fa: Either): fa is Right { return fa instanceof Right } @@ -217,16 +223,16 @@ export function right(a: A): Either { return new Right(a) } -export function fromPredicate(predicate: Predicate, l: Function1): Function1> { +export function fromPredicate(predicate: Predicate, l: (a: A) => L): (a: A) => Either { return a => predicate(a) ? right(a) : left(l(a)) } -export function mapLeft(f: Function1, fa: HKTEither): Either { - return (fa as Either).mapLeft(f) +export function mapLeft(f: (l: L) => L2, fa: Either): Either { + return fa.mapLeft(f) } -export function toOption(fa: HKTEither): Option { - return (fa as Either).toOption() +export function toOption(fa: Either): Option { + return fa.toOption() } export function tryCatch(f: Lazy): Either { @@ -237,78 +243,15 @@ export function tryCatch(f: Lazy): Either { } } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor, A>): Either - lift(functor: StaticFunctor>, f: Function1): Function1, Either> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: FantasyApply, Function1>, fa: FantasyApply, A>): Either - liftA2(apply: StaticApply>, f: Curried2): Function2, Either, Either> - liftA3(apply: StaticApply>, f: Curried3): Function3, Either, Either, Either> - liftA4(apply: StaticApply>, f: Curried4): Function4, Either, Either, Either, Either> - } -} - -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, A, B>, fa: FantasyMonad, A>): Either - flatten(mma: FantasyMonad, FantasyMonad, A>>): Either - } -} - -declare module './Foldable' { - interface FoldableOps { - reduce(f: Function2, b: B, fa: FantasyFoldable, A>): B - foldMap(monoid: StaticMonoid, f: Function1, fa: FantasyFoldable, A>): M - } -} - -declare module './Traversable' { - interface TraversableOps { - traverse(applicative: StaticApplicative, f: Function1>, ta: FantasyTraversable, A>): HKT> - - sequence( - applicative: StaticApplicative, - tfa: FantasyFoldable, HKT>): HKT> - - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable>, - tfa: HKTEither>): HKT> - } -} - -declare module './Alt' { - interface AltOps { - alt(fx: FantasyAlt, A>, fy: FantasyAlt, A>): Either - } -} - -declare module './Extend' { - interface ExtendOps { - extend(f: Cokleisli, A, B>, ea: FantasyExtend, A>): Either - } -} - -declare module './Bifunctor' { - interface BifunctorOps { - bimap(f: Function1, g: Function1, fac: FantasyBifunctor): Either - } -} - // tslint:disable-next-line no-unused-expression ;( { map, of, ap, chain, reduce, traverse, bimap, alt, extend, chainRec } as ( - StaticMonad> & - StaticFoldable> & - StaticTraversable> & + StaticMonad & + StaticFoldable & + StaticTraversable & StaticBifunctor & - StaticAlt> & - StaticExtend> & - StaticChainRec> + StaticAlt & + StaticExtend & + StaticChainRec ) ) diff --git a/src/Extend.ts b/src/Extend.ts index 1b9a6eac6..1232c06ea 100644 --- a/src/Extend.ts +++ b/src/Extend.ts @@ -1,25 +1,16 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { Cokleisli, identity } from './function' -export interface StaticExtend { - extend(f: Cokleisli, ea: HKT): HKT +export interface StaticExtend { + extend(f: Cokleisli, ea: HKT[F]): HKT[F] } -export interface FantasyExtend { - extend(f: Cokleisli): FantasyExtend +export interface FantasyExtend { + extend(f: Cokleisli): HKT[F] } -export class ExtendOps { - extend(f: Cokleisli, ea: FantasyExtend): FantasyExtend - extend(f: Cokleisli, ea: FantasyExtend): FantasyExtend { - return ea.extend(f) - } - - // TODO - duplicate(ma: FantasyExtend): FantasyExtend> - duplicate(ma: FantasyExtend): FantasyExtend> { - return ma.extend(identity) as any - } +export function duplicate(extend: StaticExtend): (ma: HKT2[F]) => HKT2[F]>[F] +export function duplicate(extend: StaticExtend): (ma: HKT[F]) => HKT[F]>[F] +export function duplicate(extend: StaticExtend): (ma: HKT[F]) => HKT[F]>[F] { + return (ma: HKT[F]) => extend.extend[F]>(identity, ma) } - -export const ops = new ExtendOps() diff --git a/src/Foldable.ts b/src/Foldable.ts index 61eb109dc..588ebd699 100644 --- a/src/Foldable.ts +++ b/src/Foldable.ts @@ -1,32 +1,21 @@ -import { HKT } from './HKT' -import { StaticMonoid } from './Monoid' -import { Function1, Function2 } from './function' +import { HKT, HKTS } from './HKT' +import { StaticMonoid, monoidArray } from './Monoid' -export interface StaticFoldable { - reduce(f: Function2, b: B, fa: HKT): B +export interface StaticFoldable { + readonly URI: F + reduce(f: (b: B, a: A) => B, b: B, fa: HKT[F]): B } -export interface FantasyFoldable extends HKT { - reduce(f: Function2, b: B): B +export interface FantasyFoldable { + reduce(f: (b: B, a: A) => B, b: B): B } -export class FoldableOps { - /** A default implementation of `foldMap` using `foldl`. */ - foldMap(monoid: StaticMonoid, f: Function1, fa: FantasyFoldable): M - foldMap(monoid: StaticMonoid, f: Function1, fa: FantasyFoldable): M { - return fa.reduce((acc, x) => monoid.concat(f(x), acc), monoid.empty()) - } - - reduce(f: Function2, b: B, fa: FantasyFoldable): B - reduce(f: Function2, b: B, fa: FantasyFoldable): B { - return fa.reduce(f, b) - } - - /** A default implementation of `foldMap` using `foldl`. */ - foldMapS(foldable: StaticFoldable, monoid: StaticMonoid, f: Function1, fa: HKT): M - foldMapS(foldable: StaticFoldable, monoid: StaticMonoid, f: Function1, fa: HKT): M { - return foldable.reduce((acc, x) => monoid.concat(f(x), acc), monoid.empty(), fa) - } +/** A default implementation of `foldMap` using `foldl`. */ +export function foldMap(foldable: StaticFoldable, monoid: StaticMonoid, f: (a: A) => M, fa: HKT[F]): M +export function foldMap(foldable: StaticFoldable, monoid: StaticMonoid, f: (a: A) => M, fa: HKT[F]): M { + return foldable.reduce((acc, x: A) => monoid.concat(f(x), acc), monoid.empty(), fa) } -export const ops = new FoldableOps() +export function toArray(foldable: StaticFoldable, fa: HKT[F]): Array { + return foldMap, A>(foldable, monoidArray, a => [a], fa) +} diff --git a/src/Functor.ts b/src/Functor.ts index f31aa5c82..8f9e153cc 100644 --- a/src/Functor.ts +++ b/src/Functor.ts @@ -1,27 +1,16 @@ -import { HKT } from './HKT' -import { Function1 } from './function' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' -export interface StaticFunctor { - map(f: Function1, fa: HKT): HKT +export interface StaticFunctor { + readonly URI: F + map(f: (a: A) => B, fa: HKT[F]): HKT[F] } -export interface FantasyFunctor extends HKT { - map(f: Function1): FantasyFunctor +export interface FantasyFunctor { + map(f: (a: A) => B): HKT[F] } -export class FunctorOps { - // module augmentation doesn't work for Arrays (its map method overloading has low priority) - // I define an explicit overloading here - map(f: Function1, fa: Array): Array - map(f: Function1, fa: FantasyFunctor): FantasyFunctor - map(f: Function1, fa: FantasyFunctor): FantasyFunctor { - return fa.map(f) - } - - lift(functor: StaticFunctor, f: Function1): Function1, HKT> - lift(functor: StaticFunctor, f: Function1): Function1, HKT> { - return fa => functor.map(f, fa) - } +export function lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT2[F]) => HKT2[F] +export function lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT[F]) => HKT[F] +export function lift(functor: StaticFunctor, f: (a: A) => B): (fa: HKT[F]) => HKT[F] { + return fa => functor.map(f, fa) } - -export const ops = new FunctorOps() diff --git a/src/HKT.ts b/src/HKT.ts index 9b747e5a7..b74b7b630 100644 --- a/src/HKT.ts +++ b/src/HKT.ts @@ -1,6 +1,7 @@ -export interface HKT { - readonly _hkt: F - readonly _hkta: A -} +export interface HKT {} -export type HKT2 = HKT, B> +export interface HKT2 {} + +export type HKTS = keyof HKT + +export type HKT2S = keyof HKT2 diff --git a/src/IO.ts b/src/IO.ts index 218cf820b..eeed782d5 100644 --- a/src/IO.ts +++ b/src/IO.ts @@ -1,31 +1,35 @@ -import { HKT } from './HKT' import { StaticMonoid } from './Monoid' import { StaticSemigroup } from './Semigroup' import { StaticMonad, FantasyMonad } from './Monad' -import { constant, Lazy, Function1, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli } from './function' +import { constant, Lazy } from './function' -export type URI = 'IO' +declare module './HKT' { + interface HKT { + IO: IO + } +} -export type HKTIO = HKT +export const URI = 'IO' + +export type URI = typeof URI export class IO implements FantasyMonad { static of = of - readonly _hkt: URI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: Lazy) {} run(): A { return this.value() } - map(f: Function1): IO { + map(f: (a: A) => B): IO { return new IO(() => f(this.run())) } of(b: B): IO { return of(b) } - ap(fab: IO>): IO { + ap(fab: IO<(a: A) => B>): IO { return new IO(() => fab.run()(this.run())) } - chain(f: Function1>): IO { + chain(f: (a: A) => IO): IO { return new IO(() => f(this.run()).run()) } concat(semigroup: StaticSemigroup, fy: IO): IO { @@ -33,28 +37,24 @@ export class IO implements FantasyMonad { } } -export function to(fa: HKTIO): IO { - return fa as IO +export function map(f: (a: A) => B, fa: IO): IO { + return fa.map(f) } -export function map(f: Function1, fa: HKTIO): IO { - return (fa as IO).map(f) -} - -export function ap(fab: IO>, fa: HKTIO): IO { - return (fa as IO).ap(fab) +export function ap(fab: IO<(a: A) => B>, fa: IO): IO { + return fa.ap(fab) } export function of(a: A): IO { return new IO(() => a) } -export function chain(f: Function1>, fa: HKTIO): IO { - return (fa as IO).chain(f as Function1>) +export function chain(f: (a: A) => IO, fa: IO): IO { + return fa.chain(f) } -export function concat(semigroup: StaticSemigroup, fx: HKTIO, fy: HKTIO): IO { - return (fx as IO).concat(semigroup, fy as IO) +export function concat(semigroup: StaticSemigroup, fx: IO, fy: IO): IO { + return fx.concat(semigroup, fy) } export function getSemigroup(semigroup: StaticSemigroup): StaticSemigroup> { @@ -66,29 +66,6 @@ export function getMonoid(monoid: StaticMonoid): StaticMonoid> { return { empty: constant(of(empty)), concat: getSemigroup(monoid).concat } } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor): IO - lift(functor: StaticFunctor, f: Function1): Function1, IO> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: FantasyApply>, fa: FantasyApply): IO - liftA2(apply: StaticApply, f: Curried2): Function2, IO, IO> - liftA3(apply: StaticApply, f: Curried3): Function3, IO, IO, IO> - liftA4(apply: StaticApply, f: Curried4): Function4, IO, IO, IO, IO> - } -} - -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, fa: FantasyMonad): IO - flatten(mma: FantasyMonad>): IO - } -} - // tslint:disable-next-line no-unused-expression ;( { map, of, ap, chain } as ( diff --git a/src/Identity.ts b/src/Identity.ts index 54a57e11e..f455aced2 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -1,7 +1,6 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { StaticApplicative } from './Applicative' import { StaticMonad, FantasyMonad } from './Monad' -import { StaticMonoid } from './Monoid' import { StaticFoldable, FantasyFoldable } from './Foldable' import { StaticSetoid } from './Setoid' import { StaticTraversable, FantasyTraversable } from './Traversable' @@ -9,41 +8,47 @@ import { StaticAlt, FantasyAlt } from './Alt' import { StaticComonad, FantasyComonad } from './Comonad' import { Either } from './Either' import { StaticChainRec, tailRec } from './ChainRec' -import { Function1, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli, Cokleisli } from './function' -export type URI = 'Identity' +declare module './HKT' { + interface HKT { + Identity: Identity + } +} -export type HKTIdentity = HKT +export const URI = 'Identity' + +export type URI = typeof URI export class Identity implements FantasyMonad, - FantasyFoldable, + FantasyFoldable, FantasyTraversable, FantasyAlt, FantasyComonad { static of = of static extract = extract - readonly _hkt: URI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: A) {} - map(f: Function1): Identity { + map(f: (a: A) => B): Identity { return new Identity(f(this.value)) } of(b: B): Identity { return of(b) } - ap(fab: Identity>): Identity { + ap(fab: Identity<(a: A) => B>): Identity { return this.map(fab.extract()) } - chain(f: Function1>): Identity { + chain(f: (a: A) => Identity): Identity { return f(this.extract()) } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return f(b, this.value) } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.map(b => of(b), f(this.value)) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.map>(of, f(this.value)) } alt(fx: Identity): Identity { return this @@ -51,10 +56,10 @@ export class Identity implements extract(): A { return this.value } - extend(f: Function1, B>): Identity { + extend(f: (ea: Identity) => B): Identity { return of(f(this)) } - fold(f: Function1): B { + fold(f: (a: A) => B): B { return f(this.value) } equals(setoid: StaticSetoid, fy: Identity): boolean { @@ -68,111 +73,52 @@ export class Identity implements } } -export function to(fa: HKTIdentity): Identity { - return fa as Identity +export function equals(setoid: StaticSetoid, fx: Identity, fy: Identity): boolean { + return fx.equals(setoid, fy as Identity) } -export function equals(setoid: StaticSetoid, fx: HKTIdentity, fy: HKTIdentity): boolean { - return (fx as Identity).equals(setoid, fy as Identity) -} - -export function map(f: Function1, fa: HKTIdentity): Identity { - return (fa as Identity).map(f) +export function map(f: (a: A) => B, fa: Identity): Identity { + return fa.map(f) } export function of(a: A): Identity { return new Identity(a) } -export function ap(fab: HKTIdentity>, fa: Identity): Identity { - return (fa as Identity).ap(fab as Identity>) +export function ap(fab: Identity<(a: A) => B>, fa: Identity): Identity { + return fa.ap(fab) } -export function chain(f: Function1>, fa: Identity): Identity { - return (fa as Identity).chain(f as Function1>) +export function chain(f: (a: A) => Identity, fa: Identity): Identity { + return fa.chain(f) } -export function reduce(f: Function2, b: B, fa: HKTIdentity): B { - return (fa as Identity).reduce(f, b) +export function reduce(f: (b: B, a: A) => B, b: B, fa: Identity): B { + return fa.reduce(f, b) } -export function alt(fx: HKTIdentity, fy: HKTIdentity): Identity { - return (fx as Identity).alt(fy as Identity) +export function alt(fx: Identity, fy: Identity): Identity { + return fx.alt(fy) } -export function traverse(applicative: StaticApplicative, f: Function1>, ta: HKTIdentity): HKT> { - return (ta as Identity).traverse(applicative, f) +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F], ta: Identity) => HKT2>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Identity) => HKT>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Identity) => HKT>[F] { + return (f: (a: A) => HKT[F], ta: Identity) => ta.traverse(applicative)(f) } -export function extend(f: Function1, B>, ea: HKTIdentity): Identity { - return (ea as Identity).extend(f) +export function extend(f: (ea: Identity) => B, ea: Identity): Identity { + return ea.extend(f) } -export function extract(fa: HKTIdentity): A { - return (fa as Identity).extract() +export function extract(fa: Identity): A { + return fa.extract() } export function chainRec(f: (a: A) => Identity>, a: A): Identity { return new Identity(tailRec(a => f(a).extract(), a)) } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor): Identity - lift(functor: StaticFunctor, f: Function1): Function1, Identity> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: Identity>, fa: FantasyApply): Identity - liftA2(apply: StaticApply, f: Curried2): Function2, Identity, Identity> - liftA3(apply: StaticApply, f: Curried3): Function3, Identity, Identity, Identity> - liftA4(apply: StaticApply, f: Curried4): Function4, Identity, Identity, Identity, Identity> - } -} - -declare module './Monad' { - interface MonadOps { - chain(f: Kleisli, fa: FantasyMonad): Identity - flatten(mma: FantasyMonad>): Identity - } -} - -declare module './Foldable' { - interface FoldableOps { - reduce(f: Function2, b: B, fa: FantasyFoldable): B - foldMap(monoid: StaticMonoid, f: Function1, fa: FantasyFoldable): M - } -} - -declare module './Traversable' { - interface TraversableOps { - traverse(applicative: StaticApplicative, f: Function1>, ta: FantasyTraversable): HKT> - - sequence( - applicative: StaticApplicative, - tfa: FantasyTraversable>): HKT> - - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable, - tfa: HKTIdentity>): HKT> - } -} - -declare module './Alt' { - interface AltOps { - alt(fx: FantasyAlt, fy: FantasyAlt): Identity - } -} - -declare module './Extend' { - interface ExtendOps { - extend(f: Cokleisli, ea: FantasyExtend): Identity - } -} - // tslint:disable-next-line no-unused-expression ;( { map, of, ap, chain, reduce, traverse, alt, extract, extend, chainRec } as ( diff --git a/src/Monad.ts b/src/Monad.ts index 964cf1506..eaaba8a08 100644 --- a/src/Monad.ts +++ b/src/Monad.ts @@ -1,12 +1,7 @@ -import { StaticApplicative } from './Applicative' -import { StaticChain } from './Chain' -import { Function1, Kleisli } from './function' +import { HKTS } from './HKT' +import { StaticApplicative, FantasyApplicative } from './Applicative' +import { StaticChain, FantasyChain } from './Chain' -export interface StaticMonad extends StaticApplicative, StaticChain {} +export interface StaticMonad extends StaticApplicative, StaticChain {} -export interface FantasyMonad { - map(f: Function1): FantasyMonad - of(b: B): FantasyMonad - ap(fab: FantasyMonad>): FantasyMonad - chain(f: Kleisli): FantasyMonad -} +export interface FantasyMonad extends FantasyApplicative, FantasyChain {} diff --git a/src/Monoid.ts b/src/Monoid.ts index d5e978b1e..ff58cd0de 100644 --- a/src/Monoid.ts +++ b/src/Monoid.ts @@ -1,7 +1,11 @@ -import { StaticSemigroup, getProductStaticSemigroup, getDualStaticSemigroup } from './Semigroup' +import { StaticSemigroup, getProductStaticSemigroup, getDualStaticSemigroup, fold as foldSemigroup } from './Semigroup' -export interface StaticMonoid extends StaticSemigroup { - empty(): M +export interface StaticMonoid extends StaticSemigroup { + empty(): A +} + +export function fold(monoid: StaticMonoid, as: Array): A { + return foldSemigroup(monoid, monoid.empty(), as) } export function getProductStaticMonoid(amonoid: StaticMonoid, bmonoid: StaticMonoid): StaticMonoid<[A, B]> { diff --git a/src/NonEmptyArray.ts b/src/NonEmptyArray.ts index 94dd05f2d..d0d6d6578 100644 --- a/src/NonEmptyArray.ts +++ b/src/NonEmptyArray.ts @@ -1,25 +1,28 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { StaticMonad, FantasyMonad } from './Monad' -import { StaticMonoid } from './Monoid' import { StaticSemigroup } from './Semigroup' import { StaticFoldable, FantasyFoldable } from './Foldable' import { StaticApplicative } from './Applicative' import { StaticTraversable, FantasyTraversable } from './Traversable' -import { Function1, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli } from './function' import * as array from './Array' import { Option, some, none } from './Option' -export type URI = 'NonEmptyArray' +declare module './HKT' { + interface HKT { + NonEmptyArray: NonEmptyArray + } +} + +export const URI = 'NonEmptyArray' -export type HKTNonEmptyArray = HKT +export type URI = typeof URI export class NonEmptyArray implements FantasyMonad, - FantasyFoldable, + FantasyFoldable, FantasyTraversable { - readonly _hkt: URI - readonly _hkta: A + readonly _URI: URI constructor(public readonly head: A, public readonly tail: Array) {} toArray(): Array { return [this.head].concat(this.tail) @@ -27,33 +30,31 @@ export class NonEmptyArray implements concatArray(as: Array): NonEmptyArray { return new NonEmptyArray(this.head, this.tail.concat(as)) } - map(f: Function1): NonEmptyArray { + map(f: (a: A) => B): NonEmptyArray { return new NonEmptyArray(f(this.head), this.tail.map(f)) } of(b: B): NonEmptyArray { return of(b) } - ap(fab: NonEmptyArray>): NonEmptyArray { - return fab.chain(f => map(f, this)) // <= derived + ap(fab: NonEmptyArray<(a: A) => B>): NonEmptyArray { + return fab.chain(f => this.map(f)) // <= derived } - chain(f: Function1>): NonEmptyArray { + chain(f: (a: A) => NonEmptyArray): NonEmptyArray { return f(this.head).concatArray(array.chain(a => f(a).toArray(), this.tail)) } concat(y: NonEmptyArray): NonEmptyArray { return this.concatArray(y.toArray()) } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return array.reduce(f, b, this.toArray()) } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.map(as => unsafeFromArray(as), array.traverse(applicative, f, this.toArray())) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.map((bs: Array) => unsafeFromArray(bs), array.traverse(applicative)(f, this.toArray())) } } -export function to(fa: HKTNonEmptyArray): NonEmptyArray { - return fa as NonEmptyArray -} - function unsafeFromArray(as: Array): NonEmptyArray { return new NonEmptyArray(as[0], as.slice(1)) } @@ -62,78 +63,34 @@ export function fromArray(as: Array): Option> { return as.length ? some(unsafeFromArray(as)) : none } -export function map(f: Function1, fa: HKTNonEmptyArray): NonEmptyArray { - return (fa as NonEmptyArray).map(f) +export function map(f: (a: A) => B, fa: NonEmptyArray): NonEmptyArray { + return fa.map(f) } -export function ap(fab: HKTNonEmptyArray>, fa: HKTNonEmptyArray): NonEmptyArray { - return (fa as NonEmptyArray).ap((fab as NonEmptyArray>)) +export function ap(fab: NonEmptyArray<(a: A) => B>, fa: NonEmptyArray): NonEmptyArray { + return fa.ap(fab) } export function of(a: A): NonEmptyArray { return new NonEmptyArray(a, []) } -export function chain(f: Function1>, fa: HKTNonEmptyArray): NonEmptyArray { - return (fa as NonEmptyArray).chain(f as Function1>) -} - -export function concat(fx: HKTNonEmptyArray, fy: HKTNonEmptyArray): NonEmptyArray { - return (fx as NonEmptyArray).concat(fy as NonEmptyArray) -} - -export function reduce(f: Function2, b: B, fa: HKTNonEmptyArray): B { - return (fa as NonEmptyArray).reduce(f, b) -} - -export function traverse(applicative: StaticApplicative, f: Function1>, ta: HKTNonEmptyArray): HKT> { - return (ta as NonEmptyArray).traverse(applicative, f) +export function chain(f: (a: A) => NonEmptyArray, fa: NonEmptyArray): NonEmptyArray { + return fa.chain(f) } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor): NonEmptyArray - lift(functor: StaticFunctor, f: Function1): Function1, NonEmptyArray> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: NonEmptyArray>, fa: FantasyApply): NonEmptyArray - liftA2(apply: StaticApply, f: Curried2): Function2, NonEmptyArray, NonEmptyArray> - liftA3(apply: StaticApply, f: Curried3): Function3, NonEmptyArray, NonEmptyArray, NonEmptyArray> - liftA4(apply: StaticApply, f: Curried4): Function4, NonEmptyArray, NonEmptyArray, NonEmptyArray, Option> - } +export function concat(fx: NonEmptyArray, fy: NonEmptyArray): NonEmptyArray { + return fx.concat(fy) } -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, fa: FantasyMonad): NonEmptyArray - flatten(mma: NonEmptyArray>): NonEmptyArray - } +export function reduce(f: (b: B, a: A) => B, b: B, fa: NonEmptyArray): B { + return fa.reduce(f, b) } -declare module './Foldable' { - interface FoldableOps { - reduce(f: Function2, b: B, fa: FantasyFoldable): B - foldMap(monoid: StaticMonoid, f: Function1, fa: FantasyFoldable): M - foldMapS(foldable: StaticFoldable, monoid: StaticMonoid, f: Function1, fa: HKTNonEmptyArray): M - } -} - -declare module './Traversable' { - interface TraversableOps { - traverse(applicative: StaticApplicative, f: Function1>, ta: FantasyTraversable): HKT> - - sequence( - applicative: StaticApplicative, - tfa: FantasyTraversable>): HKT> - - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable, - tfa: HKTNonEmptyArray>): HKT> - } +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F], ta: NonEmptyArray) => HKT2>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: NonEmptyArray) => HKT>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: NonEmptyArray) => HKT>[F] { + return (f: (a: A) => HKT[F], ta: NonEmptyArray) => ta.traverse(applicative)(f) } // tslint:disable-next-line no-unused-expression diff --git a/src/Option.ts b/src/Option.ts index 23650f346..9575a9033 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -1,26 +1,31 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { StaticMonoid } from './Monoid' import { StaticApplicative } from './Applicative' import { StaticSemigroup } from './Semigroup' import { StaticMonad, FantasyMonad } from './Monad' import { StaticFoldable, FantasyFoldable } from './Foldable' import { StaticPlus } from './Plus' -import { StaticAlternative } from './Alternative' import { StaticExtend, FantasyExtend } from './Extend' import { StaticSetoid } from './Setoid' import { StaticTraversable, FantasyTraversable } from './Traversable' -import { FantasyAlternative } from './Alternative' -import { identity, constant, constFalse, constTrue, Lazy, Function1, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli, Cokleisli, Predicate} from './function' +import { StaticAlternative, FantasyAlternative } from './Alternative' +import { identity, constant, constFalse, constTrue, Lazy, Predicate } from './function' -export type URI = 'Option' +declare module './HKT' { + interface HKT { + Option: Option + } +} -export type HKTOption = HKT +export const URI = 'Option' + +export type URI = typeof URI export type Option = None | Some export class None implements FantasyMonad, - FantasyFoldable, + FantasyFoldable, FantasyTraversable, FantasyAlternative, FantasyExtend { @@ -30,30 +35,31 @@ export class None implements static zero = zero static value: Option = new None() readonly _tag: 'None' - readonly _hkt: URI - readonly _hkta: A + readonly _URI: URI constructor() { if (none) { return none as any } } - map(f: Function1): Option { + map(f: (a: A) => B): Option { return none } of(b: B): Option { return of(b) } - ap(fab: Option>): Option { + ap(fab: Option<(a: A) => B>): Option { return none } - chain(f: Function1>): Option { + chain(f: (a: A) => Option): Option { return none } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return b } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.of(none) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.of(none) } zero(): Option { return zero() @@ -61,10 +67,10 @@ export class None implements alt(fa: Option): Option { return fa } - extend(f: Function1, B>): Option { + extend(f: (ea: Option) => B): Option { return none } - fold(n: Lazy, s: Function1): B { + fold(n: Lazy, s: (a: A) => B): B { return n() } getOrElse(f: Lazy): A { @@ -99,7 +105,7 @@ export function empty(): Option { export class Some implements FantasyMonad, - FantasyFoldable, + FantasyFoldable, FantasyTraversable, FantasyAlternative, FantasyExtend { @@ -108,26 +114,27 @@ export class Some implements static empty = empty static zero = zero readonly _tag: 'Some' - readonly _hkt: URI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: A) {} - map(f: Function1): Option { + map(f: (a: A) => B): Option { return new Some(f(this.value)) } of(b: B): Option { return of(b) } - ap(fab: Option>): Option { + ap(fab: Option<(a: A) => B>): Option { return fab.map(f => f(this.value)) } - chain(f: Function1>): Option { + chain(f: (a: A) => Option): Option { return f(this.value) } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return this.fold(constant(b), identity) } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.map(b => some(b), f(this.value)) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.map>(some, f(this.value)) } zero(): Option { return zero() @@ -135,10 +142,10 @@ export class Some implements alt(fa: Option): Option { return this } - extend(f: Function1, B>): Option { + extend(f: (ea: Option) => B): Option { return some(f(this)) } - fold(n: Lazy, s: Function1): B { + fold(n: Lazy, s: (a: A) => B): B { return s(this.value) } getOrElse(f: Lazy): A { @@ -161,56 +168,54 @@ export class Some implements } } -export function to(fa: HKTOption): Option { - return fa as Option +export function equals(setoid: StaticSetoid, fx: Option, fy: Option): boolean { + return fx.equals(setoid, fy) } -export function equals(setoid: StaticSetoid, fx: HKTOption, fy: HKTOption): boolean { - return (fx as Option).equals(setoid, fy as Option) -} - -export function fold(n: Lazy, s: Function1, fa: HKTOption): B { - return (fa as Option).fold(n, s) +export function fold(n: Lazy, s: (a: A) => B, fa: Option): B { + return fa.fold(n, s) } export function fromNullable(a: A | null | undefined): Option { return a == null ? none : some(a) } -export function toNullable(fa: HKTOption): A | null { - return (fa as Option).toNullable() +export function toNullable(fa: Option): A | null { + return fa.toNullable() } -export function map(f: Function1, fa: Option): Option { - return (fa as Option).map(f) +export function map(f: (a: A) => B, fa: Option): Option { + return fa.map(f) } export function of(a: A): Option { return new Some(a) } -export function ap(fab: HKTOption>, fa: HKTOption): Option { - return (fa as Option).ap(fab as Option>) +export function ap(fab: Option<(a: A) => B>, fa: Option): Option { + return fa.ap(fab) } -export function chain(f: Function1>, fa: HKTOption): Option { - return (fa as Option).chain(f as Function1>) +export function chain(f: (a: A) => Option, fa: Option): Option { + return fa.chain(f) } -export function reduce(f: Function2, b: B, fa: HKTOption): B { - return (fa as Option).reduce(f, b) +export function reduce(f: (b: B, a: A) => B, b: B, fa: Option): B { + return fa.reduce(f, b) } -export function traverse(applicative: StaticApplicative, f: Function1>, ta: HKTOption): HKT> { - return (ta as Option).traverse(applicative, f) +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F], ta: Option) => HKT2>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Option) => HKT>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Option) => HKT>[F] { + return (f: (a: A) => HKT[F], ta: Option) => ta.traverse(applicative)(f) } -export function alt(fx: HKTOption, fy: HKTOption): Option { - return (fx as Option).alt(fy as Option) +export function alt(fx: Option, fy: Option): Option { + return fx.alt(fy) } -export function extend(f: Function1, B>, ea: HKTOption): Option { - return (ea as Option).extend(f) +export function extend(f: (ea: Option) => B, ea: Option): Option { + return ea.extend(f) } /** Maybe monoid returning the leftmost non-None value */ @@ -218,8 +223,8 @@ export function getFirstStaticMonoid(): StaticMonoid> { return { empty, concat: alt } } -export function concat(semigroup: StaticSemigroup, fx: HKTOption, fy: HKTOption): Option { - return (fx as Option).concat(semigroup, fy as Option) +export function concat(semigroup: StaticSemigroup, fx: Option, fy: Option): Option { + return fx.concat(semigroup, fy) } export function getStaticSemigroup(semigroup: StaticSemigroup): StaticSemigroup> { @@ -230,81 +235,23 @@ export function getStaticMonoid(semigroup: StaticSemigroup): StaticMonoid< return { empty, concat: getStaticSemigroup(semigroup).concat } } -export function isSome(fa: HKTOption): fa is Some { +export function isSome(fa: Option): fa is Some { return fa instanceof Some } -export function isNone(fa: HKTOption): fa is None { +export function isNone(fa: Option): fa is None { return fa === none } export const some = of -export function fromPredicate(predicate: Predicate): Function1> { +export function fromPredicate(predicate: Predicate): (a: A) => Option { return a => predicate(a) ? some(a) : none } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor): Option - lift(functor: StaticFunctor, f: Function1): Function1, Option> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: Option>, fa: FantasyApply): Option - liftA2(apply: StaticApply, f: Curried2): Function2, Option, Option> - liftA3(apply: StaticApply, f: Curried3): Function3, Option, Option, Option> - liftA4(apply: StaticApply, f: Curried4): Function4, Option, Option, Option, Option> - } -} - -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, fa: FantasyMonad): Option - flatten(mma: FantasyMonad>): Option - } -} - -declare module './Foldable' { - interface FoldableOps { - reduce(f: Function2, b: B, fa: FantasyFoldable): B - foldMap(monoid: StaticMonoid, f: Function1, fa: FantasyFoldable): M - foldMapS(foldable: StaticFoldable, monoid: StaticMonoid, f: Function1, fa: HKTOption): M - } -} - -declare module './Traversable' { - interface TraversableOps { - traverse(applicative: StaticApplicative, f: Function1>, ta: FantasyTraversable): HKT> - - sequence( - applicative: StaticApplicative, - tfa: FantasyTraversable>): HKT> - - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable, - tfa: HKTOption>): HKT> - } -} - -declare module './Alt' { - interface AltOps { - alt(fx: FantasyAlt, fy: FantasyAlt): Option - } -} - -declare module './Extend' { - interface ExtendOps { - extend(f: Cokleisli, ea: FantasyExtend): Option - } -} - // tslint:disable-next-line no-unused-expression ;( - { map, of, ap, chain, reduce, traverse, alt, extend, zero } as ( + { map, of, ap, chain, reduce, traverse, zero, alt, extend } as ( StaticMonad & StaticFoldable & StaticPlus & diff --git a/src/Ord.ts b/src/Ord.ts index 53a02745c..ed7d1d6b1 100644 --- a/src/Ord.ts +++ b/src/Ord.ts @@ -5,17 +5,12 @@ import { setoidNumber, setoidString } from './Setoid' -import { Function2 } from './function' - -export type Comparator = Function2 - -export type NativeComparator = Function2 export interface StaticOrd extends StaticSetoid { compare(x: A, y: A): Ordering } -export function toNativeComparator(compare: Comparator): NativeComparator { +export function toNativeComparator(compare: (x: A, y: A) => Ordering): (x: A, y: A) => number { return (x, y) => { const c = compare(x, y) return c === 'GT' ? 1 : c === 'EQ' ? 0 : -1 diff --git a/src/Plus.ts b/src/Plus.ts index 5a90a5655..c890ecb40 100644 --- a/src/Plus.ts +++ b/src/Plus.ts @@ -1,13 +1,10 @@ -import { HKT } from './HKT' -import { StaticAlt } from './Alt' -import { Function1 } from './function' +import { HKT, HKTS } from './HKT' +import { StaticAlt, FantasyAlt } from './Alt' -export interface StaticPlus extends StaticAlt { - zero(): HKT +export interface StaticPlus extends StaticAlt { + zero(): HKT[F] } -export interface FantasyPlus { - map(f: Function1): FantasyPlus - alt(fy: FantasyPlus): FantasyPlus +export interface FantasyPlus extends FantasyAlt { zero(): FantasyPlus } diff --git a/src/Pointed.ts b/src/Pointed.ts new file mode 100644 index 000000000..37aa444f7 --- /dev/null +++ b/src/Pointed.ts @@ -0,0 +1,10 @@ +import { HKT, HKTS } from './HKT' +import { StaticFunctor, FantasyFunctor } from './Functor' + +export interface StaticPointed extends StaticFunctor { + of(a: A): HKT[F] +} + +export interface FantasyPointed extends FantasyFunctor { + of(a: A): HKT[F] +} diff --git a/src/PointedFunctor.ts b/src/PointedFunctor.ts deleted file mode 100644 index 785fa321f..000000000 --- a/src/PointedFunctor.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { HKT } from './HKT' -import { StaticFunctor } from './Functor' - -export interface StaticPointedFunctor extends StaticFunctor { - of(a: A): HKT -} diff --git a/src/Reader.ts b/src/Reader.ts index 281245ebc..dfdb6ceb8 100644 --- a/src/Reader.ts +++ b/src/Reader.ts @@ -1,53 +1,54 @@ -import { HKT } from './HKT' import { StaticMonad, FantasyMonad } from './Monad' -import { identity, Function1, Endomorphism, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli } from './function' +import { identity, Endomorphism } from './function' -export type URI = 'Reader' +declare module './HKT' { + interface HKT { + Reader: Reader + } + interface HKT2 { + Reader: Reader + } +} -export type HKTURI = HKT +export const URI = 'Reader' -export type HKTReader = HKT, A> +export type URI = typeof URI -export class Reader implements FantasyMonad, A> { +export class Reader implements FantasyMonad { static of = of - readonly _hkt: HKTURI - readonly _hkta: A - constructor(public readonly value: Function1) {} + readonly _URI: URI + constructor(public readonly value: (e: E) => A) {} run(e: E): A { return this.value(e) } - map(f: Function1): Reader { + map(f: (a: A) => B): Reader { return new Reader((e: E) => f(this.run(e))) } of(b: B): Reader { return of(b) } - ap(fab: Reader>): Reader { + ap(fab: Reader B>): Reader { return new Reader((e: E) => fab.run(e)(this.run(e))) } - chain(f: Function1>): Reader { + chain(f: (a: A) => Reader): Reader { return new Reader((e: E) => f(this.run(e)).run(e)) } } -export function to(fa: HKTReader): Reader { - return fa as Reader -} - -export function map(f: Function1, fa: HKTReader): Reader { - return (fa as Reader).map(f) +export function map(f: (a: A) => B, fa: Reader): Reader { + return fa.map(f) } export function of(a: A): Reader { return new Reader((e: E) => a) } -export function ap(fab: HKTReader>, fa: HKTReader): Reader { - return (fa as Reader).ap(fab as Reader>) +export function ap(fab: Reader B>, fa: Reader): Reader { + return fa.ap(fab) } -export function chain(f: Function1>, fa: HKTReader): Reader { - return (fa as Reader).chain(f as Function1>) +export function chain(f: (a: A) => Reader, fa: Reader): Reader { + return fa.chain(f) } /** reads the current context */ @@ -56,41 +57,18 @@ export function ask(): Reader { } /** Projects a value from the global context in a Reader */ -export function asks(f: Function1): Reader { +export function asks(f: (e: E) => A): Reader { return new Reader(f) } /** changes the value of the local context during the execution of the action `fa` */ -export function local(f: Endomorphism, fa: HKTReader): Reader { - return new Reader((e: E) => (fa as Reader).run(f(e))) -} - -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor, A>): Reader - lift(functor: StaticFunctor>, f: Function1): Function1, Reader> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: Reader>, fa: FantasyApply, A>): Reader - liftA2(apply: StaticApply>, f: Curried2): Function2, Reader, Reader> - liftA3(apply: StaticApply>, f: Curried3): Function3, Reader, Reader, Reader> - liftA4(apply: StaticApply>, f: Curried4): Function4, Reader, Reader, Reader, Reader> - } -} - -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, A, B>, fa: FantasyMonad, A>): Reader - flatten(mma: FantasyMonad, FantasyMonad, A>>): Reader - } +export function local(f: Endomorphism, fa: Reader): Reader { + return new Reader((e: E) => fa.run(f(e))) } // tslint:disable-next-line no-unused-expression ;( { map, of, ap, chain } as ( - StaticMonad> + StaticMonad ) ) diff --git a/src/Semigroup.ts b/src/Semigroup.ts index 169fe952b..3e45806a9 100644 --- a/src/Semigroup.ts +++ b/src/Semigroup.ts @@ -1,5 +1,9 @@ -export interface StaticSemigroup { - concat(x: M, y: M): M +export interface StaticSemigroup { + concat(x: A, y: A): A +} + +export function fold(semigroup: StaticSemigroup, a: A, as: Array): A { + return as.reduce((acc, a) => semigroup.concat(acc, a), a) } export function getFirstStaticSemigroup(): StaticSemigroup { diff --git a/src/State.ts b/src/State.ts index 55ec9893b..1c4ea1012 100644 --- a/src/State.ts +++ b/src/State.ts @@ -1,17 +1,22 @@ -import { HKT } from './HKT' import { StaticMonad, FantasyMonad } from './Monad' -import { Function1, Endomorphism, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli } from './function' +import { Endomorphism } from './function' -export type URI = 'State' +declare module './HKT' { + interface HKT { + State: State + } + interface HKT2 { + State: State + } +} -export type HKTURI = HKT +export const URI = 'State' -export type HKTState = HKT, A> +export type URI = typeof URI -export class State implements FantasyMonad, A> { - readonly _hkt: HKTURI - readonly _hkta: A - constructor(public readonly value: Function1) {} +export class State implements FantasyMonad { + readonly _URI: URI + constructor(public readonly value: (s: S) => [A, S]) {} run(s: S): [A, S] { return this.value(s) } @@ -21,7 +26,7 @@ export class State implements FantasyMonad, A> { exec(s: S): S { return this.run(s)[1] } - map(f: Function1): State { + map(f: (a: A) => B): State { return new State(s => { const [a, s1] = this.run(s) return [f(a), s1] @@ -30,10 +35,10 @@ export class State implements FantasyMonad, A> { of(b: B): State { return of(b) } - ap(fab: State>): State { - return fab.chain(f => map(f, this)) // <= derived + ap(fab: State B>): State { + return fab.chain(f => this.map(f)) // <= derived } - chain(f: Function1>): State { + chain(f: (a: A) => State): State { return new State(s => { const [a, s1] = this.run(s) return f(a).run(s1) @@ -41,24 +46,20 @@ export class State implements FantasyMonad, A> { } } -export function to(fa: HKTState): State { - return fa as State +export function map(f: (a: A) => B, fa: State): State { + return fa.map(f) } -export function map(f: Function1, fa: HKTState): State { - return (fa as State).map(f) -} - -export function ap(fab: HKTState>, fa: HKTState): State { - return (fa as State).ap(fab as State>) +export function ap(fab: State B>, fa: State): State { + return fa.ap(fab) } export function of(a: A): State { return new State(s => [a, s]) } -export function chain(f: Function1>, fa: HKTState): State { - return (fa as State).chain(f as Function1>) +export function chain(f: (a: A) => State, fa: State): State { + return fa.chain(f) } export function get(): State { @@ -73,36 +74,13 @@ export function modify(f: Endomorphism): State { return new State(s => [undefined, f(s)]) } -export function gets(f: Function1): State { +export function gets(f: (s: S) => A): State { return new State(s => [f(s), s]) } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor, A>): State - lift(functor: StaticFunctor>, f: Function1): Function1, State> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: State>, fa: FantasyApply, A>): State - liftA2(apply: StaticApply>, f: Curried2): Function2, State, State> - liftA3(apply: StaticApply>, f: Curried3): Function3, State, State, State> - liftA4(apply: StaticApply>, f: Curried4): Function4, State, State, State, State> - } -} - -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, A, B>, fa: FantasyMonad, A>): State - flatten(mma: FantasyMonad, FantasyMonad, A>>): State - } -} - // tslint:disable-next-line no-unused-expression ;( { map, of, ap, chain } as ( - StaticMonad> + StaticMonad ) ) diff --git a/src/Task.ts b/src/Task.ts index f8a2bcd39..c5b060666 100644 --- a/src/Task.ts +++ b/src/Task.ts @@ -1,31 +1,35 @@ -import { HKT } from './HKT' import { StaticMonoid } from './Monoid' import { StaticMonad, FantasyMonad } from './Monad' -import { Lazy, Function1, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli } from './function' +import { Lazy } from './function' -export type URI = 'Task' +declare module './HKT' { + interface HKT { + Task: Task + } +} -export type HKTTask = HKT +export const URI = 'Task' + +export type URI = typeof URI export class Task implements FantasyMonad { static of = of static empty = empty - readonly _hkt: URI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: Lazy>) {} run(): Promise { return this.value() } - map(f: Function1): Task { + map(f: (a: A) => B): Task { return new Task(() => this.run().then(f)) } of(b: B): Task { return of(b) } - ap(fab: Task>): Task { + ap(fab: Task<(a: A) => B>): Task { return new Task(() => Promise.all([fab.run(), this.run()]).then(([f, a]) => f(a))) } - chain(f: Function1>): Task { + chain(f: (a: A) => Task): Task { return new Task(() => this.run().then(a => f(a).run())) } concat(fy: Task): Task { @@ -45,28 +49,24 @@ export class Task implements FantasyMonad { } } -export function to(fa: HKTTask): Task { - return fa as Task -} - -export function map(f: Function1, fa: HKTTask): Task { - return (fa as Task).map(f) +export function map(f: (a: A) => B, fa: Task): Task { + return fa.map(f) } export function of(a: A): Task { return new Task(() => Promise.resolve(a)) } -export function ap(fab: HKTTask>, fa: HKTTask): Task { - return (fa as Task).ap(fab as Task>) +export function ap(fab: Task<(a: A) => B>, fa: Task): Task { + return fa.ap(fab) } -export function chain(f: Function1>, fa: HKTTask): Task { - return (fa as Task).chain(f as Function1>) +export function chain(f: (a: A) => Task, fa: Task): Task { + return fa.chain(f) } -export function concat(fx: HKTTask, fy: HKTTask): Task { - return (fx as Task).concat(fy as Task) +export function concat(fx: Task, fy: Task): Task { + return fx.concat(fy) } const neverPromise = new Promise(resolve => undefined) @@ -78,29 +78,6 @@ export function empty(): Task { return never as Task } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor): Task - lift(functor: StaticFunctor, f: Function1): Function1, Task> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: Task>, fa: FantasyApply): Task - liftA2(apply: StaticApply, f: Curried2): Function2, Task, Task> - liftA3(apply: StaticApply, f: Curried3): Function3, Task, Task, Task> - liftA4(apply: StaticApply, f: Curried4): Function4, Task, Task, Task, Task> - } -} - -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, fa: FantasyMonad): Task - flatten(mma: FantasyMonad>): Task - } -} - // tslint:disable-next-line no-unused-expression ;( { map, of, ap, chain, concat, empty } as ( diff --git a/src/Traced.ts b/src/Traced.ts index 2c2fe2b81..2e6444193 100644 --- a/src/Traced.ts +++ b/src/Traced.ts @@ -1,28 +1,32 @@ -import { HKT } from './HKT' import { StaticMonoid } from './Monoid' import { StaticComonad, FantasyComonad } from './Comonad' -import { Function1, Cokleisli } from './function' -export type URI = 'Traced' +declare module './HKT' { + interface HKT { + Traced: Traced + } + interface HKT2 { + Traced: Traced + } +} -export type HKTURI = HKT +export const URI = 'Traced' -export type HKTTraced = HKT, A> +export type URI = typeof URI -export class Traced implements FantasyComonad, A> { - readonly _hkt: HKTURI - readonly _hkta: A - constructor(public readonly monoid: StaticMonoid, public readonly value: Function1) { } +export class Traced implements FantasyComonad { + readonly _URI: URI + constructor(public readonly monoid: StaticMonoid, public readonly value: (e: E) => A) { } run(e: E): A { return this.value(e) } - map(f: Function1): Traced { + map(f: (a: A) => B): Traced { return new Traced(this.monoid, e => f(this.run(e))) } extract(): A { return this.run(this.monoid.empty()) } - extend(f: Function1, B>): Traced { + extend(f: (ea: Traced) => B): Traced { return new Traced( this.monoid, m1 => f( @@ -37,38 +41,21 @@ export class Traced implements FantasyComonad, A> { } } -export function to(fa: HKTTraced): Traced { - return fa as Traced +export function map(f: (a: A) => B, ea: Traced): Traced { + return ea.map(f) } -export function map(f: Function1, ea: HKTTraced): Traced { - return (ea as Traced).map(f) +export function extract(ea: Traced): A { + return ea.extract() } -export function extract(ea: HKTTraced): A { - return (ea as Traced).extract() -} - -export function extend(f: Function1, B>, ea: HKTTraced): Traced { - return (ea as Traced).extend(f as Function1, B>) -} - -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor, A>): Traced - lift(functor: StaticFunctor>, f: Function1): Function1, Traced> - } -} - -declare module './Extend' { - interface ExtendOps { - extend(f: Cokleisli, A, B>, ea: FantasyExtend, A>): Traced - } +export function extend(f: (ea: Traced) => B, ea: Traced): Traced { + return ea.extend(f) } // tslint:disable-next-line no-unused-expression ;( { map, extract, extend } as ( - StaticComonad> + StaticComonad ) ) diff --git a/src/Traversable.ts b/src/Traversable.ts index 8ee672cf6..c25e127b2 100644 --- a/src/Traversable.ts +++ b/src/Traversable.ts @@ -1,42 +1,19 @@ -import { HKT } from './HKT' -import { StaticFunctor } from './Functor' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' +import { StaticFunctor, FantasyFunctor } from './Functor' import { StaticFoldable, FantasyFoldable } from './Foldable' import { StaticApplicative } from './Applicative' -import { identity, Function1 } from './function' +import { identity } from './function' -export interface StaticTraversable extends StaticFunctor, StaticFoldable { - traverse(applicative: StaticApplicative, f: Function1>, ta: HKT): HKT> +export interface StaticTraversable extends StaticFunctor, StaticFoldable { + traverse(applicative: StaticApplicative/*, x?: never*/): (f: (a: A) => HKT[F], ta: HKT[T]) => HKT[T]>[F] } -export interface FantasyTraversable extends FantasyFoldable { - traverse(applicative: StaticApplicative, f: Function1>): HKT> +export interface FantasyTraversable extends FantasyFunctor, FantasyFoldable { + traverse(applicative: StaticApplicative/*, x?: never*/): (f: (a: A) => HKT[F]) => HKT[T]>[F] } -export class TraversableOps { - traverse(applicative: StaticApplicative, f: Function1>, ta: FantasyTraversable): HKT> - traverse(applicative: StaticApplicative, f: Function1>, ta: FantasyTraversable): HKT> { - return ta.traverse(applicative, f) - } - - sequence( - applicative: StaticApplicative, - tfa: FantasyTraversable>): HKT> - sequence( - applicative: StaticApplicative, - tfa: FantasyTraversable>): HKT> { - return tfa.traverse(applicative, identity) - } - - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable, - tfa: HKT>): HKT> - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable, - tfa: HKT>): HKT> { - return traversable.traverse, A>(applicative, identity, tfa) - } +export function sequence(applicative: StaticApplicative, traversable: StaticTraversable): (tfa: HKT[F]>[T]) => HKT2[T]>[F] +export function sequence(applicative: StaticApplicative, traversable: StaticTraversable): (tfa: HKT[F]>[T]) => HKT[T]>[F] +export function sequence(applicative: StaticApplicative, traversable: StaticTraversable): (tfa: HKT[F]>[T]) => HKT[T]>[F] { + return (tfa: HKT[F]>[T]) => traversable.traverse(applicative)[F], A>(identity, tfa) } - -export const ops = new TraversableOps() diff --git a/src/Unfoldable.ts b/src/Unfoldable.ts index 35f6443de..dc026afca 100644 --- a/src/Unfoldable.ts +++ b/src/Unfoldable.ts @@ -1,40 +1,40 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { StaticApplicative } from './Applicative' import { StaticTraversable } from './Traversable' import * as option from './Option' -import { ops } from './Traversable' +import { sequence } from './Traversable' import { constant } from './function' /** This class identifies data structures which can be _unfolded_, * generalizing `unfoldr` on arrays. */ -export interface StaticUnfoldable { - unfoldr(f: (b: B) => option.HKTOption<[A, B]>, b: B): HKT +export interface StaticUnfoldable { + readonly URI: F + unfoldr(f: (b: B) => option.Option<[A, B]>, b: B): HKT[F] } /** Replicate a value some natural number of times. */ -export function replicate(unfoldable: StaticUnfoldable, n: number, a: A): HKT { - function step(n: number): option.HKTOption<[A, number]> { +export function replicate(unfoldable: StaticUnfoldable, n: number, a: A): HKT2[F] +export function replicate(unfoldable: StaticUnfoldable, n: number, a: A): HKT[F] +export function replicate(unfoldable: StaticUnfoldable, n: number, a: A): HKT[F] { + function step(n: number): option.Option<[A, number]> { return n <= 0 ? option.none : option.of<[A, number]>([a, n - 1]) } return unfoldable.unfoldr(step, n) } /** Perform an Applicative action `n` times, and accumulate all the results. */ -export function replicateA( - applicative: StaticApplicative, - unfoldableTraversable: StaticUnfoldable & StaticTraversable, - n: number, - ma: HKT - ): HKT> { - return ops.sequenceS(applicative, unfoldableTraversable, replicate(unfoldableTraversable, n, ma)) +export function replicateA(applicative: StaticApplicative, unfoldableTraversable: StaticUnfoldable & StaticTraversable): (n: number, ma: HKT2[F]) => HKT2[T]>[F] +export function replicateA(applicative: StaticApplicative, unfoldableTraversable: StaticUnfoldable & StaticTraversable): (n: number, ma: HKT[F]) => HKT[T]>[F] +export function replicateA(applicative: StaticApplicative, unfoldableTraversable: StaticUnfoldable & StaticTraversable): (n: number, ma: HKT[F]) => HKT[T]>[F] { + return (n: number, ma: HKT[F]) => sequence(applicative, unfoldableTraversable)(replicate(unfoldableTraversable, n, ma)) } /** The container with no elements - unfolded with zero iterations. */ -export function none(unfoldable: StaticUnfoldable): HKT { +export function none(unfoldable: StaticUnfoldable): HKT[F] { return unfoldable.unfoldr(constant(option.none), undefined) } -export function singleton(unfoldable: StaticUnfoldable, a: A): HKT { +export function singleton(unfoldable: StaticUnfoldable, a: A): HKT[F] { return replicate(unfoldable, 1, a) } diff --git a/src/Validation.ts b/src/Validation.ts index 6efb241f5..28971113f 100644 --- a/src/Validation.ts +++ b/src/Validation.ts @@ -1,6 +1,6 @@ -import { HKT } from './HKT' +import { HKT, HKTS, HKT2, HKT2S } from './HKT' import { StaticFunctor } from './Functor' -import { StaticPointedFunctor } from './PointedFunctor' +import { StaticPointed } from './Pointed' import { StaticApplicative } from './Applicative' import { StaticSemigroup } from './Semigroup' import { FantasyApply } from './Apply' @@ -8,56 +8,63 @@ import { StaticFoldable, FantasyFoldable } from './Foldable' import { StaticSetoid } from './Setoid' import { StaticTraversable, FantasyTraversable } from './Traversable' import { StaticAlt, FantasyAlt } from './Alt' -import { StaticMonoid } from './Monoid' -import { constFalse, constTrue, Function1, Function2, Function3, Function4, Predicate, Curried2, Curried3, Curried4 } from './function' +import { constFalse, constTrue, Predicate } from './function' import { Option, some, none } from './Option' import { Either, left, right } from './Either' import * as nea from './NonEmptyArray' -export type URI = 'Validation' +declare module './HKT' { + interface HKT { + Validation: Validation + } + interface HKT2 { + Validation: Validation + } +} -export type HKTURI = HKT +export const URI = 'Validation' -export type HKTValidation = HKT, A> +export type URI = typeof URI export type Validation = Failure | Success export class Failure implements - FantasyApply, A>, - FantasyFoldable, A>, - FantasyTraversable, A>, - FantasyAlt, A> { + FantasyApply, + FantasyFoldable, + FantasyTraversable, + FantasyAlt { static of = of readonly _tag: 'Failure' - readonly _hkt: HKTURI - readonly _hkta: A + readonly _URI: URI constructor(public readonly semigroup: StaticSemigroup, public readonly value: L) {} - map(f: Function1): Validation { + map(f: (a: A) => B): Validation { return this as any } of(b: B): Validation { return of(b) } - ap(fab: Validation>): Validation { + ap(fab: Validation B>): Validation { if (isFailure(fab)) { return failure(this.semigroup, this.semigroup.concat(fab.value, this.value)) } return this as any } - bimap(semigroup: StaticSemigroup, f: Function1, g: Function1): Validation { + bimap(semigroup: StaticSemigroup, f: (l: L) => L2, g: (a: A) => B): Validation { return failure(semigroup, f(this.value)) } alt(fy: Validation): Validation { return fy } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return b } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.of(this as any) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.of(this as any) } - fold(failure: Function1, success: Function1): B { + fold(failure: (l: L) => B, success: (a: A) => B): B { return failure(this.value) } equals(setoid: StaticSetoid, fy: Validation): boolean { @@ -69,7 +76,7 @@ export class Failure implements () => this ) } - mapFailure(semigroup: StaticSemigroup, f: Function1): Validation { + mapFailure(semigroup: StaticSemigroup, f: (l: L) => L2): Validation { return failure(semigroup, f(this.value)) } swap(semigroup: StaticSemigroup): Validation { @@ -82,7 +89,7 @@ export class Failure implements return left(this.value) } /** Lift the Invalid value into a NonEmptyArray */ - toValidationNea(): Option, A>> { + toEitherNea(): Option, A>> { return some(failure, A>(nea, nea.of(this.value))) } inspect() { @@ -94,41 +101,42 @@ export class Failure implements } export class Success implements - FantasyApply, A>, - FantasyFoldable, A>, - FantasyTraversable, A>, - FantasyAlt, A> { + FantasyApply, + FantasyFoldable, + FantasyTraversable, + FantasyAlt { static of = of readonly _tag: 'Success' - readonly _hkt: HKTURI - readonly _hkta: A + readonly _URI: URI constructor(public readonly value: A) {} - map(f: Function1): Validation { + map(f: (a: A) => B): Validation { return new Success(f(this.value)) } of(b: B): Validation { return of(b) } - ap(fab: Validation>): Validation { + ap(fab: Validation B>): Validation { if (isSuccess(fab)) { return this.map(fab.value) } return fab as any } - bimap(semigroup: StaticSemigroup, f: Function1, g: Function1): Validation { + bimap(semigroup: StaticSemigroup, f: (l: L) => L2, g: (a: A) => B): Validation { return new Success(g(this.value)) } alt(fy: Validation): Validation { return this } - reduce(f: Function2, b: B): B { + reduce(f: (b: B, a: A) => B, b: B): B { return f(b, this.value) } - traverse(applicative: StaticApplicative, f: Function1>): HKT> { - return applicative.map(b => of(b), f(this.value)) + traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F]) => HKT2>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] + traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F]) => HKT>[F] { + return (f: (a: A) => HKT[F]) => applicative.map((b: B) => of(b), f(this.value)) } - fold(failure: Function1, success: Function1): B { + fold(failure: (l: L) => B, success: (a: A) => B): B { return success(this.value) } equals(setoid: StaticSetoid, fy: Validation): boolean { @@ -137,7 +145,7 @@ export class Success implements concat(fy: Validation): Validation { return this } - mapFailure(semigroup: StaticSemigroup, f: Function1): Validation { + mapFailure(semigroup: StaticSemigroup, f: (l: L) => L2): Validation { return this as any } swap(semigroup: StaticSemigroup): Validation { @@ -150,7 +158,7 @@ export class Success implements return right(this.value) } /** Lift the Invalid value into a NonEmptyArray */ - toValidationNea(): Option, A>> { + toEitherNea(): Option, A>> { return none } inspect() { @@ -161,55 +169,53 @@ export class Success implements } } -export function to(fa: HKTValidation): Validation { - return fa as Validation +export function equals(setoid: StaticSetoid, fx: Validation, fy: Validation): boolean { + return fx.equals(setoid, fy) } -export function equals(setoid: StaticSetoid, fx: HKTValidation, fy: HKTValidation): boolean { - return (fx as Validation).equals(setoid, (fy as Validation)) +export function fold(failure: (l: L) => B, success: (a: A) => B, fa: Validation): B { + return fa.fold(failure, success) } -export function fold(failure: Function1, success: Function1, fa: HKTValidation): B { - return (fa as Validation).fold(failure, success) -} - -export function map(f: Function1, fa: HKTValidation): Validation { - return (fa as Validation).map(f) +export function map(f: (a: A) => B, fa: Validation): Validation { + return fa.map(f) } export function of(a: A): Success { return new Success(a) } -export function getApplicativeS(semigroup: StaticSemigroup): StaticApplicative> { - function ap(fab: HKTValidation>, fa: HKTValidation): Validation { - return (fa as Validation).ap(fab as Validation>) +export function getApplicativeS(semigroup: StaticSemigroup): StaticApplicative { + function ap(fab: Validation B>, fa: Validation): Validation { + return fa.ap(fab as Validation B>) } - return { map, of, ap } + return { URI, map, of, ap } } -export function bimap(semigroup: StaticSemigroup, f: Function1, g: Function1, fa: HKTValidation): Validation { - return (fa as Validation).bimap(semigroup, f, g) +export function bimap(semigroup: StaticSemigroup, f: (l: L) => L2, g: (a: A) => B, fa: Validation): Validation { + return fa.bimap(semigroup, f, g) } -export function alt(fx: HKTValidation, fy: HKTValidation): Validation { - return (fx as Validation).alt(fy as Validation) +export function alt(fx: Validation, fy: Validation): Validation { + return fx.alt(fy as Validation) } -export function reduce(f: Function2, b: B, fa: HKTValidation): B { - return (fa as Validation).reduce(f, b) +export function reduce(f: (b: B, a: A) => B, b: B, fa: Validation): B { + return fa.reduce(f, b) } -export function traverse(applicative: StaticApplicative, f: Function1>, ta: HKTValidation): HKT> { - return (ta as Validation).traverse(applicative, f) +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT2[F], ta: Validation) => HKT2>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Validation) => HKT>[F] +export function traverse(applicative: StaticApplicative): (f: (a: A) => HKT[F], ta: Validation) => HKT>[F] { + return (f: (a: A) => HKT[F], ta: Validation) => ta.traverse(applicative)(f) } -export function isFailure(fa: HKTValidation): fa is Failure { +export function isFailure(fa: Validation): fa is Failure { return fa instanceof Failure } -export function isSuccess(fa: HKTValidation): fa is Success { +export function isSuccess(fa: Validation): fa is Success { return fa instanceof Success } @@ -221,85 +227,41 @@ export function success(a: A): Success { return new Success(a) } -export function fromPredicate(semigroup: StaticSemigroup, predicate: Predicate, l: Function1): (a: A) => Validation { +export function fromPredicate(semigroup: StaticSemigroup, predicate: Predicate, l: (a: A) => L): (a: A) => Validation { return a => predicate(a) ? success(a) : failure(semigroup, l(a)) } -export function concat(fx: HKTValidation, fy: HKTValidation): Validation { - return (fx as Validation).concat((fy as Validation)) -} - -export function mapFailure(semigroup: StaticSemigroup, f: Function1, fa: HKTValidation): Validation { - return (fa as Validation).mapFailure(semigroup, f) -} - -export function swap(semigroup: StaticSemigroup, fa: HKTValidation): Validation { - return (fa as Validation).swap(semigroup) -} - -export function toOption(fa: HKTValidation): Option { - return (fa as Validation).toOption() +export function concat(fx: Validation, fy: Validation): Validation { + return fx.concat(fy) } -export function toEither(fa: HKTValidation): Either { - return (fa as Validation).toEither() +export function mapFailure(semigroup: StaticSemigroup, f: (l: L) => L2, fa: Validation): Validation { + return fa.mapFailure(semigroup, f) } -export function toValidationNea(fa: HKTValidation): Option, A>> { - return (fa as Validation).toValidationNea() +export function swap(semigroup: StaticSemigroup, fa: Validation): Validation { + return fa.swap(semigroup) } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor, A>): Validation - lift(functor: StaticFunctor>, f: Function1): Function1, Validation> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: Validation>, fa: FantasyApply, A>): Validation - liftA2(apply: StaticApply>, f: Curried2): Function2, Validation, Validation> - liftA3(apply: StaticApply>, f: Curried3): Function3, Validation, Validation, Validation> - liftA4(apply: StaticApply>, f: Curried4): Function4, Validation, Validation, Validation, Validation> - } +export function toOption(fa: Validation): Option { + return fa.toOption() } -declare module './Foldable' { - interface FoldableOps { - reduce(f: Function2, b: B, fa: FantasyFoldable, A>): B - foldMap(monoid: StaticMonoid, f: Function1, fa: Validation): M - } +export function toEither(fa: Validation): Either { + return fa.toEither() } -declare module './Traversable' { - interface TraversableOps { - traverse(applicative: StaticApplicative, f: Function1>, ta: FantasyTraversable, A>): HKT> - - sequence( - applicative: StaticApplicative, - tfa: FantasyTraversable, HKT>): HKT> - - sequenceS( - applicative: StaticApplicative, - traversable: StaticTraversable>, - tfa: HKTValidation>): HKT> - } -} - -declare module './Alt' { - interface AltOps { - alt(fx: FantasyAlt, A>, fy: FantasyAlt, A>): Validation - } +export function toEitherNea(fa: Validation): Option, A>> { + return fa.toEitherNea() } // tslint:disable-next-line no-unused-expression ;( { map, of, reduce, traverse, alt } as ( - StaticFunctor> & - StaticPointedFunctor> & - StaticFoldable> & - StaticTraversable> & - StaticAlt> + StaticFunctor & + StaticPointed & + StaticFoldable & + StaticTraversable & + StaticAlt ) ) diff --git a/src/Writer.ts b/src/Writer.ts index 842f8494b..36b060b59 100644 --- a/src/Writer.ts +++ b/src/Writer.ts @@ -1,19 +1,24 @@ -import { HKT } from './HKT' import { StaticMonoid } from './Monoid' import { StaticFunctor } from './Functor' import { StaticMonad, FantasyMonad } from './Monad' -import { Lazy, Function1, Function2, Function3, Function4, Curried2, Curried3, Curried4, Kleisli } from './function' +import { Lazy } from './function' -export type URI = 'Writer' +declare module './HKT' { + interface HKT { + Writer: Writer + } + interface HKT2 { + Writer: Writer + } +} -export type HKTURI = HKT +export const URI = 'Writer' -export type HKTWriter = HKT, A> +export type URI = typeof URI -export class Writer implements FantasyMonad, A> { - _hkt: HKTURI - _hkta: A - of: Function1> +export class Writer implements FantasyMonad { + readonly _URI: URI + of: (a: A) => Writer constructor(public readonly monoid: StaticMonoid, public readonly value: Lazy<[A, W]>) { this.of = of(monoid) } @@ -26,14 +31,14 @@ export class Writer implements FantasyMonad, A> { exec(): W { return this.run()[1] } - map(f: Function1): Writer { + map(f: (a: A) => B): Writer { const [a, w] = this.run() return new Writer(this.monoid, () => [f(a), w]) } - ap(fab: Writer>): Writer { + ap(fab: Writer B>): Writer { return fab.chain(f => this.map(f)) } - chain(f: Function1>): Writer { + chain(f: (a: A) => Writer): Writer { return new Writer(this.monoid, () => { const [a, w1] = this.run() const [b, w2] = f(a).run() @@ -42,32 +47,29 @@ export class Writer implements FantasyMonad, A> { } } -export function to(fa: HKTWriter): Writer { - return fa as Writer -} - -export function map(f: Function1, fa: HKTWriter): Writer { - return (fa as Writer).map(f) +export function map(f: (a: A) => B, fa: Writer): Writer { + return fa.map(f) } export function of(monoid: StaticMonoid): (a: A) => Writer { return (a: A) => new Writer(monoid, () => [a, monoid.empty()]) } -export function ap(fab: HKTWriter>, fa: HKTWriter): Writer { - return (fa as Writer).ap(fab as Writer>) +export function ap(fab: Writer B>, fa: Writer): Writer { + return fa.ap(fab) } -export function chain(f: Function1>, fa: HKTWriter): Writer { - return (fa as Writer).chain(f as Function1>) +export function chain(f: (a: A) => Writer, fa: Writer): Writer { + return fa.chain(f) } export function tell(monoid: StaticMonoid): (w: W) => Writer { return w => new Writer(monoid, () => [undefined, w]) } -export function getMonadS(monoid: StaticMonoid): StaticMonad> { +export function getMonadS(monoid: StaticMonoid): StaticMonad { return { + URI, map, of: of(monoid), ap, @@ -75,32 +77,9 @@ export function getMonadS(monoid: StaticMonoid): StaticMonad> { } } -declare module './Functor' { - interface FunctorOps { - map(f: Function1, fa: FantasyFunctor, A>): Writer - lift(functor: StaticFunctor>, f: Function1): Function1, Writer> - } -} - -declare module './Apply' { - interface ApplyOps { - ap(fab: Writer>, fa: FantasyApply, A>): Writer - liftA2(apply: StaticApply>, f: Curried2): Function2, Writer, Writer> - liftA3(apply: StaticApply>, f: Curried3): Function3, Writer, Writer, Writer> - liftA4(apply: StaticApply>, f: Curried4): Function4, Writer, Writer, Writer, Writer> - } -} - -declare module './Chain' { - interface MonadOps { - chain(f: Kleisli, A, B>, fa: FantasyMonad, A>): Writer - flatten(mma: FantasyMonad, FantasyMonad, A>>): Writer - } -} - // tslint:disable-next-line no-unused-expression ;( { map } as ( - StaticFunctor> + StaticFunctor ) ) diff --git a/src/function.ts b/src/function.ts index 92ffee3e3..c3f69e79f 100644 --- a/src/function.ts +++ b/src/function.ts @@ -1,4 +1,4 @@ -import { HKT } from './HKT' +import { HKT, HKTS } from './HKT' export type Lazy = () => A @@ -21,16 +21,18 @@ export type Curried7 = (a: A) => (b: B) => (c: C) => (d: export type Curried8 = (a: A) => (b: B) => (c: C) => (d: D) => (e: E) => (f: F) => (g: G) => (h: H) => I export type Curried9 = (a: A) => (b: B) => (c: C) => (d: D) => (e: E) => (f: F) => (g: G) => (h: H) => (i: I) => J -export type Predicate = Function1 +export type Predicate = (a: A) => boolean -export type Endomorphism = Function1 +export function not(predicate: Predicate): Predicate { + return a => !predicate(a) +} -export type BinaryOperation = Function2 +export type Endomorphism = (a: A) => A -export type ClosedBinaryOperation = BinaryOperation +export type BinaryOperation = Function2 -export type Kleisli = Function1> -export type Cokleisli = Function1, B> +export type Kleisli = (a: A) => HKT[F] +export type Cokleisli = (fa: HKT[F]) => B export function constant(a: A): Lazy { return () => a @@ -49,18 +51,18 @@ export function flip(f: Function2): Function2 { } /** The `on` function is used to change the domain of a binary operator. */ -export function on(op: BinaryOperation, f: Function1): BinaryOperation { +export function on(op: BinaryOperation, f: (a: A) => B): BinaryOperation { return (x, y) => op(f(x), f(y)) } -export function compose(bc: Function1, ab: Function1): Function1 -export function compose(cd: Function1, bc: Function1, ab: Function1): Function1 -export function compose(de: Function1, cd: Function1, bc: Function1, ab: Function1): Function1 -export function compose(ef: Function1, de: Function1, cd: Function1, bc: Function1, ab: Function1): Function1 -export function compose(fg: Function1, ef: Function1, de: Function1, cd: Function1, bc: Function1, ab: Function1): Function1 -export function compose(gh: Function1, fg: Function1, ef: Function1, de: Function1, cd: Function1, bc: Function1, ab: Function1): Function1 -export function compose(hi: Function1, gh: Function1, fg: Function1, ef: Function1, de: Function1, cd: Function1, bc: Function1, ab: Function1): Function1 -export function compose(ij: Function1, hi: Function1, gh: Function1, fg: Function1, ef: Function1, de: Function1, cd: Function1, bc: Function1, ab: Function1): Function1 +export function compose(bc: (b: B) => C, ab: (a: A) => B): (a: A) => C +export function compose(cd: (c: C) => D, bc: (b: B) => C, ab: (a: A) => B): (a: A) => D +export function compose(de: (d: D) => E, cd: (c: C) => D, bc: (b: B) => C, ab: (a: A) => B): (a: A) => E +export function compose(ef: (e: E) => F, de: (d: D) => E, cd: (c: C) => D, bc: (b: B) => C, ab: (a: A) => B): (a: A) => F +export function compose(fg: (f: F) => G, ef: (e: E) => F, de: (d: D) => E, cd: (c: C) => D, bc: (b: B) => C, ab: (a: A) => B): (a: A) => G +export function compose(gh: (g: G) => H, fg: (f: F) => G, ef: (e: E) => F, de: (d: D) => E, cd: (c: C) => D, bc: (b: B) => C, ab: (a: A) => B): (a: A) => H +export function compose(hi: (h: H) => I, gh: (g: G) => H, fg: (f: F) => G, ef: (e: E) => F, de: (d: D) => E, cd: (c: C) => D, bc: (b: B) => C, ab: (a: A) => B): (a: A) => I +export function compose(ij: (i: I) => J, hi: (h: H) => I, gh: (g: G) => H, fg: (f: F) => G, ef: (e: E) => F, de: (d: D) => E, cd: (c: C) => D, bc: (b: B) => C, ab: (a: A) => B): (a: A) => J export function compose(...fns: Array): Function { const len = fns.length - 1 return function(this: any, x: any) { @@ -72,14 +74,14 @@ export function compose(...fns: Array): Function { } } -export function pipe(ab: Function1, bc: Function1): Function1 -export function pipe(ab: Function1, bc: Function1, cd: Function1): Function1 -export function pipe(ab: Function1, bc: Function1, cd: Function1, de: Function1): Function1 -export function pipe(ab: Function1, bc: Function1, cd: Function1, de: Function1, ef: Function1): Function1 -export function pipe(ab: Function1, bc: Function1, cd: Function1, de: Function1, ef: Function1, fg: Function1): Function1 -export function pipe(ab: Function1, bc: Function1, cd: Function1, de: Function1, ef: Function1, fg: Function1, gh: Function1): Function1 -export function pipe(ab: Function1, bc: Function1, cd: Function1, de: Function1, ef: Function1, fg: Function1, gh: Function1, hi: Function1): Function1 -export function pipe(ab: Function1, bc: Function1, cd: Function1, de: Function1, ef: Function1, fg: Function1, gh: Function1, hi: Function1, ij: Function1): Function1 +export function pipe(ab: (a: A) => B, bc: (b: B) => C): (a: A) => C +export function pipe(ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): (a: A) => D +export function pipe(ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): (a: A) => E +export function pipe(ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): (a: A) => F +export function pipe(ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): (a: A) => G +export function pipe(ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H): (a: A) => H +export function pipe(ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I): (a: A) => I +export function pipe(ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I, ij: (i: I) => J): (a: A) => J export function pipe(...fns: Array): Function { const len = fns.length - 1 return function(this: any, x: any) { diff --git a/src/index.ts b/src/index.ts index 9ec70f4d1..14097ff76 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,20 +4,20 @@ export { StaticApplicative, FantasyApplicative } from './Applicative' export { StaticApply, FantasyApply } from './Apply' export { StaticBifunctor, FantasyBifunctor } from './Bifunctor' export { StaticChain, FantasyChain } from './Chain' -export { StaticChainRec } from './ChainRec' +export { StaticChainRec, FantasyChainRec } from './ChainRec' export { StaticComonad, FantasyComonad } from './Comonad' export { StaticContravariant, FantasyContravariant } from './Contravariant' export { StaticCopointed, FantasyCopointed } from './Copointed' export { StaticExtend, FantasyExtend } from './Extend' export { StaticFoldable, FantasyFoldable } from './Foldable' export { StaticFunctor, FantasyFunctor } from './Functor' -export { HKT } from './HKT' +export { HKT, HKTS, HKT2, HKT2S } from './HKT' export { StaticMonad, FantasyMonad } from './Monad' export { StaticMonoid } from './Monoid' export { StaticOrd } from './Ord' export { Ordering } from './Ordering' export { StaticPlus, FantasyPlus } from './Plus' -export { StaticPointedFunctor } from './PointedFunctor' +export { StaticPointed, FantasyPointed } from './Pointed' export { StaticSemigroup } from './Semigroup' export { StaticSetoid } from './Setoid' export { StaticTraversable, FantasyTraversable } from './Traversable' diff --git a/test/Apply.ts b/test/Apply.ts index 588dbde39..fef497a07 100644 --- a/test/Apply.ts +++ b/test/Apply.ts @@ -1,5 +1,7 @@ import { - ops + liftA2, + liftA3, + liftA4 } from '../src/Apply' import * as option from '../src/Option' import * as either from '../src/Either' @@ -9,7 +11,20 @@ describe('Apply', () => { it('liftA2', () => { const f = (a: number) => (b: number) => a + b - eqOptions(ops.liftA2(option, f)(option.some(2), option.some(3)), option.some(5)) - eqEithers(ops.liftA2(either, f)(either.right(2), either.right(3)), either.right(5)) + eqOptions(liftA2(option, f)(option.some(2), option.some(3)), option.some(5)) + eqEithers(liftA2(either, f)(either.right(2), either.right(3)), either.right(5)) }) + + it('liftA3', () => { + const f = (a: number) => (b: number) => (c: number) => a + b + c + eqOptions(liftA3(option, f)(option.some(2), option.some(3), option.some(4)), option.some(9)) + eqEithers(liftA3(either, f)(either.right(2), either.right(3), either.right(4)), either.right(9)) + }) + + it('liftA4', () => { + const f = (a: number) => (b: number) => (c: number) => (d: number) => a + b + c + d + eqOptions(liftA4(option, f)(option.some(2), option.some(3), option.some(4), option.some(5)), option.some(14)) + eqEithers(liftA4(either, f)(either.right(2), either.right(3), either.right(4), either.right(5)), either.right(14)) + }) + }) diff --git a/test/Array.ts b/test/Array.ts index 829a3c210..f46f3d347 100644 --- a/test/Array.ts +++ b/test/Array.ts @@ -13,10 +13,10 @@ describe('Arr', () => { it('traverse', () => { const tfanone = [1, 2] const f = (n: number): option.Option => n % 2 === 0 ? none : some(n) - const fasnone = arr.traverse(option, f, tfanone) + const fasnone = arr.traverse(option)(f, tfanone) assert.ok(option.isNone(fasnone)) const tfa = [1, 3] - const fas = arr.traverse(option, f, tfa) + const fas = arr.traverse(option)(f, tfa) eq(fas, some([1, 3])) }) diff --git a/test/Foldable.ts b/test/Foldable.ts new file mode 100644 index 000000000..9336a5150 --- /dev/null +++ b/test/Foldable.ts @@ -0,0 +1,13 @@ +import * as assert from 'assert' +import { + toArray +} from '../src/Foldable' +import * as array from '../src/Array' + +describe('Foldable', () => { + + it('toArray', () => { + assert.deepEqual(toArray(array, [1, 2, 3]), [3, 2, 1]) + }) + +}) diff --git a/test/Functor.ts b/test/Functor.ts index a72ef02bd..a44957614 100644 --- a/test/Functor.ts +++ b/test/Functor.ts @@ -1,13 +1,13 @@ import * as option from '../src/Option' -import { ops } from '../src/Functor' +import { lift } from '../src/Functor' import { eqOptions as eq } from './helpers' describe('Functor', () => { it('lift', () => { - const f = (a: number) => a * 2 - const liftedF = ops.lift(option, f) - const actual = liftedF(option.some(2)) + const double = (a: number) => a * 2 + const f = lift(option, double) + const actual = f(option.some(2)) eq(actual, option.some(4)) }) diff --git a/test/Monoid.ts b/test/Monoid.ts new file mode 100644 index 000000000..d4a808999 --- /dev/null +++ b/test/Monoid.ts @@ -0,0 +1,13 @@ +import * as assert from 'assert' +import { + fold, + monoidSum +} from '../src/Monoid' + +describe('Monoid', () => { + + it('fold', () => { + assert.strictEqual(fold(monoidSum, [1, 2, 3]), 6) + }) + +}) diff --git a/test/Option.ts b/test/Option.ts index 4d4632374..568077f35 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -12,7 +12,6 @@ import { fromNullable } from '../src/Option' import * as option from '../src/Option' -import { ops } from '../src/Traversable' import { setoidNumber } from '../src/Setoid' import { eqOptions as eq } from './helpers' @@ -78,9 +77,9 @@ describe('Option', () => { }) it('traverse', () => { - const x = some('hello').traverse(option, s => some(s.length)) + const x = some('hello').traverse(option)(s => some(s.length)) eq(x, some(some(5))) - const y = ops.traverse(option, s => some(s.length), some('hello')) + const y = some('hello').traverse(option)(s => some(s.length)) eq(y, some(some(5))) }) diff --git a/test/Semigroup.ts b/test/Semigroup.ts new file mode 100644 index 000000000..ef8b5ef49 --- /dev/null +++ b/test/Semigroup.ts @@ -0,0 +1,13 @@ +import * as assert from 'assert' +import { + fold +} from '../src/Semigroup' +import { monoidString } from '../src/Monoid' + +describe('Semigroup', () => { + + it('fold', () => { + assert.strictEqual(fold(monoidString, '' , ['a', 'b', 'c']), 'abc') + }) + +}) diff --git a/test/Unfoldable.ts b/test/Unfoldable.ts new file mode 100644 index 000000000..64511722a --- /dev/null +++ b/test/Unfoldable.ts @@ -0,0 +1,21 @@ +import * as assert from 'assert' + +import { + replicate, + replicateA +} from '../src/Unfoldable' +import * as array from '../src/Array' +import * as option from '../src/Option' +import { eqOptions as eq } from './helpers' + +describe('Unfoldable', () => { + + it('replicate', () => { + assert.deepEqual(replicate(array, 2, 's'), ['s', 's']) + }) + + it('replicateA', () => { + eq(replicateA(option, array)(2, option.some(1)), option.some([1, 1])) + }) + +}) diff --git a/test/helpers.ts b/test/helpers.ts index 80a74ae77..a5cefbb83 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -1,8 +1,8 @@ import * as assert from 'assert' -import { HKTOption, isSome, isNone } from '../src/Option' -import { HKTEither, isLeft, isRight } from '../src/Either' +import { Option, isSome, isNone } from '../src/Option' +import { Either, isLeft, isRight } from '../src/Either' -export function eqOptions(x: HKTOption, y: HKTOption) { +export function eqOptions(x: Option, y: Option) { if (isSome(x) && isSome(y)) { assert.deepEqual(x.value, y.value) } else { @@ -10,7 +10,7 @@ export function eqOptions(x: HKTOption, y: HKTOption) { } } -export function eqEithers(x: HKTEither, y: HKTEither) { +export function eqEithers(x: Either, y: Either) { if (isRight(x) && isRight(y)) { assert.deepEqual(x.value, y.value) } else if (isLeft(x) && isLeft(y)) { diff --git a/typings-checker/index.ts b/typings-checker/index.ts new file mode 100644 index 000000000..ce695391c --- /dev/null +++ b/typings-checker/index.ts @@ -0,0 +1,110 @@ +import * as either from '../src/Either' +import * as option from '../src/Option' +import * as id from '../src/Identity' +import * as array from '../src/Array' +import * as nea from '../src/NonEmptyArray' + +import { FantasyFunctor } from '../src/Functor' +import { HKTS } from '../src/HKT' + +const len = (s: string): number => s.length +const double = (n: number): number => n * 2 +const sum = (a: number) => (b: number): number => a + b + +// +// Functor +// + +import { lift } from '../src/Functor' + +const liftOption = lift(option, double) +const liftEither = lift(either, len) + +declare function fantasyFunctor(fanstasyFunctor: FantasyFunctor): void + +// +// Apply +// + +import { liftA2 } from '../src/Apply' + +const liftA2Option = liftA2(option, sum) +const liftA2Either = liftA2(either, sum) + +// +// Chain +// + +import { flatten } from '../src/Chain' + +const e5 = flatten(either)(either.right>(either.right(1))) + +// +// Traversable +// + +import { sequence } from '../src/Traversable' + +const t1 = sequence(option, array)([option.some(1)]) +const t2 = sequence(either, array)([either.right(1)]) + +// +// Unfoldable +// + +import { replicate, replicateA } from '../src/Unfoldable' + +const a3 = replicate(array, 2, 's') +const o5 = replicate(array, 2, option.some(1)) +const e6 = replicate(array, 2, either.right(1)) +const o6 = replicateA(option, array)(2, option.some(1)) +const e7 = replicateA(either, array)(2, either.right(1)) + +// +// Option +// + +const o1 = option.some('s').traverse(option)(s => option.some(s.length)) +const o2 = option.some('s').traverse(either)(s => either.right(s.length)) +const o3 = option.traverse(option)(s => option.some(s.length), option.of('s')) +const o4 = option.traverse(either)(s => either.right(s.length), option.of('s')) + +fantasyFunctor(option.of(1)) + +// +// Either +// + +const e1 = either.right('s').traverse(option)(s => option.some(s.length)) +const e2 = either.right('s').traverse(either)(s => either.right(s.length)) +const e3 = either.traverse(option)(s => option.some(s.length), either.right('s')) // option.Option> +const e4 = either.traverse(either)(s => either.right(s.length), either.right('s')) // either.Either> + +fantasyFunctor(either.of(1)) + +// +// Array +// + +const a1 = array.traverse(option)(s => option.some(s.length), array.of('s')) +const a2 = array.traverse(either)(s => either.right(s.length), array.of('s')) + +fantasyFunctor([1, 2, 3]) + +// +// Identity +// + +const i1 = id.traverse(option)(s => option.some(s.length), id.of('s')) +const i2 = id.traverse(either)(s => either.right(s.length), id.of('s')) + +fantasyFunctor(id.of(1)) + +// +// NonEmptyArray +// + +const nea1 = nea.traverse(option)(s => option.some(s.length), nea.of('s')) +const nea2 = nea.traverse(either)(s => either.right(s.length), nea.of('s')) + +fantasyFunctor(nea.of(1)) diff --git a/typings-checker/tsconfig.json b/typings-checker/tsconfig.json new file mode 100644 index 000000000..f28990b0e --- /dev/null +++ b/typings-checker/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "noEmit": true, + "noImplicitAny": true, + "noImplicitReturns": false, + "noImplicitThis": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "strictNullChecks": true, + "target": "es5" + } +}