Typed Static-land compliance
This document outlines a strategy that promises at the very least a guarantee a library will not become incompatible with the Static-land specification and at best allows a path towards full compatibility, without clobbering users with hard to understand terminology, such as monoids, monads and functors.
Rules
A list of Static-land reserved function names, with TypeScript types, is provided at the bottom
- No type will export a function name out of the reserved list, without also sharing type and behavior
- If a function shares the type of a Static-land reserved function and has the same behavior, either an alias is made or the method receives the same name
The first rule simply means that if you have, for example, a map
method that's not compliant with the Functor.map
, you name it transform
or select
instead. Array.map
is compliant with Functor.map
, it's actually unusual for name collisions to happen, as Static-land is based on vanilla JavaScript.
The second rule is just to make sure that if there exists, for example, a flatMap
compliant with Chain.chain
(as in many libraries), compatibility with Static-land is ensured by either having an alias or simply naming it chain
in the first place.
Fantasy-land compliance
Each type that exports Static-land compliant functions also has non-enumerable namespaced methods on the type. For example, if a map
function exists, the type itself also has a fantasy-land/map
property, which is compliant with Fantasy-land (usually the same signature as Static-land, but with this
functioning as the type to act on. By making these non-enumerable, you only come across them if you're looking (no pollution of the type). These act as a formal protocol for libraries such as Ramda, so they know how to handle that type.
Static-land reserved functions
For expected behavior, check the Static-land spec. These mostly do exactly what you expect them to do. Static-land never collapses though, so a list of lists is never automatically flattened, for example. Obviously, names of parameters are not important, signature is.
In these, Type is a stand-in for the type in question, generic parameters are noted as A, B, C, etc. A type can always have more generic parameters than specified, but never less.
equals(a: Type, b: Type): boolean
lte(a: Type, b: Type): boolean
concat(a: Type, b: Type): Type
empty(): Type
map<A, B>(f: (a: A) => B, ta: Type<A>): Type<B>
bimap<A, B, C, D>(fa: (a: A) => B, fc: (c: C) => D, ta: Type<A, C>): Type<B, D>
contramap<A, B>(f: (a: A) => B, tb: Type<B>): Type<A>
promap<A, B, C, D>(fa: (a: A) => B, fc: (c: C) => D, tbc: Type<B, C>): Type<A, D>
ap<A, B>(f: Type<(a: A) => B>, ta: Type<A>): Type<B>
of<A>(a: A): Type<A>
alt<A>(a: Type<A>, b: Type<A>): Type<A>
zero<A>(): Type<A>
chain<A, B>(f: (a: A) => Type<B>, ta: Type<A>): Type<B>
chainRec // Not easily typable in TypeScript
reduce<A, B>(f: (a: A, b: B) => A, a: A, tb: Type<B>): A
extend<A, B>(f: (ta: Type<A>) => B, ta: Type<A>): Type<B>
extract<A>(ta: Type<A>): A
traverse // Not easily typable in TypeScript
Progress
General
List
Original Post: How important is compliance to fantasy-land/static-land for this project? I'd like to use this project together with Ramda and fantasy-land libraries.
I'm willing to help out on this front