fantasyland / static-land

Specification for common algebraic structures in JavaScript based on Fantasy Land
MIT License
772 stars 41 forks source link

Fantasy Land adapter #19

Closed dead-claudia closed 8 years ago

dead-claudia commented 8 years ago

I think it may be easier to use if instead of using fromFLType, you just created generic wrappers for them. It'd be rather trivial, anyways.

rpominov commented 8 years ago

But how do we implement of and empty in a general wrapper?

dead-claudia commented 8 years ago

@rpominov

Those would be the two that you couldn't implement with a simple wrapper type, since they (unintelligently IMHO) use the instance just to create a new instance (it's nearly always aliased in libraries where that is an instance method). You could easily make simple 1-argument factories to simplify their creation, though.

The rest can, and here's an implementation (albeit untested) of several general wrappers and factories, to show what I mean:

const extend = (...parents) => Object.assign({}, ...parents)

// Factories for Applicative/Monad and Monoid, since they require an
// instance in the Fantasy Land spec, but not in Static Land.
export const makeApplicative = of => extend(Apply, {of})
export const makeMonad = Applicative => extend(Applicative, Chain)
export const makeMonoid = empty => extend(Semigroup, {empty})

export const Setoid = {equals: (x, y) => y.equals(x)}
export const Semigroup = {concat: (x, y) => y.concat(x)}
export const Functor = {map: (f, x) => x.map(f)}

export const Apply = extend(Functor)
Apply.ap = (f, x) => x.ap(f)

export const Foldable = {}
Foldable.reduce = (f, initial, x) => x.reduce(f, initial)

export const Traversable = extend(Functor, Foldable)
Traverable.sequence = (of, x) => x.sequence(of)

export const Chain = extend(Apply)
Chain.chain = (x, y) => y.chain(x)

export const Extend = {extend: (f, x) => x.extend(f)}

export const Comonad = extend(Functor, Extend)
Comonad.extract = x => x.extract()

export const Bifunctor = extend(Functor)
Bifunctor.bimap = (f, g, x) => x.bimap(f, g)

export const Profunctor = extend(Functor)
Profunctor.promap = (f, g, x) => x.promap(f, g)
rpominov commented 8 years ago

Small correction: empty don't accept any arguments https://github.com/rpominov/static-land/blob/master/docs/spec.md#monoid

Could you elaborate what we should expose in Static Land utils API instead of fromFLType in your opinion? Something like the following?

import {fantasyLandAdapters} from 'static-land'

fantasyLandAdapters.Setoid // {equals: (x, y) => y.equals(x)}
fantasyLandAdapters.Semigroup
...

fantasyLandAdapters.makeApplicative
fantasyLandAdapters.makeMonad
fantasyLandAdapters.makeMonoid

This seems more complex and harder to understand than just fromFLType helper.

dead-claudia commented 8 years ago

@rpominov

Small correction: empty don't accept any arguments https://github.com/rpominov/static-land/blob/master/docs/spec.md#monoid

Good catch! Fixed above.

Could you elaborate what we should expose in Static Land utils API instead of fromFLType in your opinion? Something like the following?

That's basically what I was proposing (the namespace is just a semantic difference). Sorry for putting it a bit in the abstract.

This seems more complex and harder to understand than just fromFLType helper.

I think you're correct, though. Would it be better to make fromFLType understand the FL hierarchy (as opposed to just the methods), and use something similar to the above proposal as basically an optimization (so everything is using the same wrappers)? It would make the utility a little larger (I'd estimate about 2-4x increase in code size), but the memory footprint would be far smaller, since everything is pulling from shared instances for most types.

rpominov commented 8 years ago

Yea, I think it's a good idea to reuse methods that can be reused. Although I'd just did something like this instead of FL hierarchy:

...

function map(fn, tx) {
  return tx[$.map](fn)
}

export default function fromFLType(...) {

  ...

  if (available($.map)) {
    Type.map = map
  }

  ...

}

Do you want to make a PR?

dead-claudia commented 8 years ago

Sure, once I get a chance. The individual methods may fare better since some types conform to multiple disjoint Fantasy Land types (e.g. monad and monoid both - streams can be implemented in such a way).

On Mon, Aug 29, 2016, 06:33 Roman Pominov notifications@github.com wrote:

Yea, I think it's good idea to reuse methods that can be reused. Although I'd just did something like this instead of FL hierarchy:

... function map(fn, tx) { return tx$.map } export default function fromFLType(...) {

...

if (available($.map)) { Type.map = map }

...

}

Do you want to make a PR?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/rpominov/static-land/issues/19#issuecomment-243089969, or mute the thread https://github.com/notifications/unsubscribe-auth/AERrBLnfObha-VLoywTJ33eHPYIDtQ7Sks5qkrV5gaJpZM4JskEc .