Skip to content

fantasy-land compliance #49

Open
Open
@SimonMeskens

Description

@SimonMeskens

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

  1. No type will export a function name out of the reserved list, without also sharing type and behavior
  2. 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

  • Refactor all isEqual functions to equals
  • Add namespaced fantasy methods
  • Write tests for the laws in Static-land

List

  • Look at unifying List with other structures #31
  • Add fold primitive to internals
  • Add List/of
  • Add List/zero
  • Add List/alt
  • Add List/map #51
  • Add List/filter
  • Add List/ap
  • Add List/reduce
  • Add List/chain
  • Add List/traverse
  • Add List/extend

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions