gcanti / flow-static-land

[DEPRECATED, please check out fp-ts] Implementation of common algebraic types in JavaScript + Flow
MIT License
408 stars 22 forks source link

Primitives vs classes #42

Closed danielepolencic closed 7 years ago

danielepolencic commented 7 years ago

Disclaimer: This is a general question and not a bug report.

This library implements most of the data type such as Maybe and Either using primitive values:

maybe.of(3) // <- 3!

Most of the (monad related) literature on the internet uses classes:

var Maybe = function(val) {
    this.__value = val;
};

Maybe.of = function(val) {
    return new Maybe(val);
};

var maybeOne = Maybe.of(1); // <- this is a Maybe instance

The advantage of classes is that can be chained very easily:

function getUserBanner(banners, user) {
    return Maybe.of(user)
        .map(prop('accountDetails'))
        .map(prop('address'))
        .map(prop('province'))
        .map(prop(R.__, banners));
}

Whereas in the case of this library, I'm forced to compose all my data types.

Don't get me wrong, I understand this is the same. It's just that I see the class approach as battery included whereas your approach needing... a little bit of help.

I found your approach intriguing, but I don't understand why I would use it over a class.

Would you mind sharing why you opted for this approach?

gcanti commented 7 years ago

Most of the (monad related) literature on the internet uses classes:

If you are referring to fantasy-land, you may want to read the static-land spec https://github.com/rpominov/static-land#difference-from-fantasy-land which includes a section about differences from fantasy-land with pros and cons.

Passing around dictionaries of methods is certainly awkward when you must do it by hand instead of relying on a compiler (like PureScript). However there are some great pros using static-land. We may also find ways to alleviate the awkwardness: for example this is a naive way to simulate the do notation

https://github.com/gcanti/flow-static-land/blob/master/src/Maybe.js#L162

// @flow

import type { Maybe } from 'flow-static-land/lib/Maybe'
import * as maybe from 'flow-static-land/lib/Maybe'

const x: Maybe<number> = maybe.Do.of(3)
  .map(n => n * 2)
  .map(n => n + 2)
  .chain(n => maybe.of(n - 1))
  .map(n => n - 3)
  .value

console.log(x) // => 4

There's also a prosaic but very important reason: Flow is pretty buggy at the moment and this implementation is way more solid and type safe than the other implementations (classes included) that I tried so far.

danielepolencic commented 7 years ago

Thanks for the link.

I noticed the do notation in Maybe and I really like it. Is this implementation buggy in Flow too?

I guess the benefit of having the do notation separated from Maybe is that you can have the best of both worlds. The only downside is that you have to call .value to unwrap the value.

Thanks for clarifying.