evilsoft / crocks

A collection of well known Algebraic Data Types for your utter enjoyment.
https://crocks.dev
ISC License
1.59k stars 102 forks source link

Missing "join" method on ADTs #388

Open mrhubbs opened 5 years ago

mrhubbs commented 5 years ago

Based on my understanding of many of the ADTs, as described here, I think crocks is missing join.

Here is a simplified use case (I realize I could create this function with more of the functional building-blocks Crocks supplies):

// { } -> String -> Either(Error String, a)
const getData = payload => {
  return field => {
    const data = payload[field]

    // hmmm..
    if (!data) {
      return Left(new Error(`Cannot get field "${data}"`))
    } else {
      return Right(data)
    }
  }
}

const result = Either(
  getData({ 10: 100, 'dogs': 'cats' })
)
.ap(Right('dogs'))

console.log(result)
// Right Right 'cats'

The problem is that the result is a Right inside a Right.

I'd like to be able to do this:

const result = Either(
  getData({ 10: 100, 'dogs': 'cats' })
)
.ap(Right('dogs'))
.join()

console.log(result)
// Right 'cats'

Am I missing something? Is there a different way to use ap that wouldn't create nested types?

evilsoft commented 5 years ago

You are correct that the for something to be an actual Monad is should have a join function, there are a couple issues with that though in JavaScript. Namely it is the fact that Array is a Monad and includes a join method that will takes an Array to a String. That was the driving factor for it not being included in this library. It would be nice to come up with a name for it other then join. Was 🌽sidering flatten, but that creates issues with proposals on Array.

The good news is, the math allows us to derive this function by simply chaining an identity function as so:

import chain from 'crocks/pointfree/chain'
import identity from 'crocks/combinators/identity'

// joinM :: Monad m => m(m a) -> m a
const joinM =
  chain(identity)

We are totally open to suggestions for how this function should be names, so if possible we would like to keep this issue open to gather suggesions. Would this joinM be helpful enough for you in the mean time while we get this naming sorted?

mrhubbs commented 5 years ago

Hi @evilsoft. Yes, that joinM will be very helpful to me. Thanks a lot!

Here are some random - probably not mathematically sound - brainstorm ideas:

On a different note, I wanted to say earlier that I'm really enjoying this library. I tried a few other functional libraries after reading this book and they felt rather abstruse. Things are really clicking for me with crocks. Thanks a lot!

evilsoft commented 5 years ago

I am a fan of fuse btw.

karthikiyengar commented 4 years ago

I have strong memories of missing this function, and this looks useful for sure. Since we're prefixing m to most array-colliding functions like mconcat, mreduce et al, we could even consider mjoin for consistency's sake? I can chalk this out if we're in agreement.

mrhubbs commented 4 years ago

I think following the established convention is a good idea, but I don’t think I have enough familiarity with the codebase to give a strong opinion.

dalefrancis88 commented 4 years ago

well @mrhubbs this is a pretty good first issue to complete if you'd like to try your hand at contributing to the codebase. @karthikiyengar looks keen to help

dalefrancis88 commented 4 years ago

@karthikiyengar the m is because it's performing that action within the context of the given monoid. Not so much that it collides with the array functions

mrhubbs commented 4 years ago

@dalefrancis88 I'd love to, thanks for the invitation. However I'm pretty busy the next few weeks. Perhaps it'd be best for @karthikiyengar to take care of it? Does this have a particular timeframe?

dalefrancis88 commented 4 years ago

Nope, it's first in best dressed really, and you're the first to ask for it. If Karth gets in there before you then it's all good, there are other things that need to be added :)