Fantasy-land: The new Apply spec

Created on 19 Sep 2016  路  8Comments  路  Source: fantasyland/fantasy-land

Did I take Crazy Pills?

More of a question than an actual issue. I think I may be missing something with the new Apply spec updated a wee bit ago. So I started to make updates to my project to reflect this change and it seems to me like this change is limiting the API. Just want to make sure I have this right. So an Identity used to be:

function Identity(x) {
  const map = fn => Identity(fn(x))
  const value = R.always(x)
  const ap = m => m.map(x)

  return { map, value, ap }
}

and I could "chain" it outside of a lift function as:

// data === Identity 8
const data =
  Identity(add)
    .ap(Identity(3))
    .ap(Identity(5))

// or

// data === Identity 8
const another =
  Identity(3)
    .map(add)
    .ap(Identity(5))

Never have to extract in the container and I have the option of having one of the parameters do the lifting for me through map, if I choose to.

But if I am reading the spec correct it means it all looks and feels like this now:

function Identity(x) {
...
  const ap = m => Identity(m.value()(x))
...
}

const data =
  Identity(4)
    .ap(
      Identity(5)
        .ap(Identity(add))
    )

Now am I missing how to use this? It seem like I lose the ability to "chain" and am always stuck with this composing interface. So if I am reading that right it seems that we lose the ability to choose how our flows are constructed and either have to provide our users lift functions to hide all of this away from them, or limit them to this interface.

Now I am _very_ new at this, so I am probably missing something really cool and clever here. But I guess I have two questions:

  1. What is the value of making this change? (or why was it wrong to begin with)
  2. Is there a better way to use this outside of writing liftAN functions.

Most helpful comment

What is the value of making this change?

Consistency.

Before:

map   :: Functor f => f a        ~>   (a -> b) -> f b
ap    ::   Apply f => f (a -> b) ~>        f a -> f b
chain ::   Chain f => f a        ~> (a -> f b) -> f b

After:

map   :: Functor f => f a ~>   (a -> b) -> f b
ap    ::   Apply f => f a ~> f (a -> b) -> f b
chain ::   Chain f => f a ~> (a -> f b) -> f b

All 8 comments

Sorry that example was kinda ugly and strawman-ish it can be used like this of course:

const data =
  Identity(4)
    .ap(Identity(5).map(add))

but I still have to compose here, yeah?

Maybe I'm old fashioned...

The one thing I really enjoyed about that crusty old ap being different than map and chain is that if I ever found myself with function on the left, i knew that it would be there for me, to set things straight. I could always rely on it to take a value and do what was right. And continue on until a value resulted Leaving me happy, know I am back to a value on the left.

But now if I ever end up with a function on the left, I have no recourse other than some fancy arrangement through composition.

(this is the last comment I swear!!)

EDIT: remove gender. Did not come off as expected!

Well, I mean, I guess I can clean it up by using thrush.
Is this what you had in mind when we have a function on the left?

const thrush =
  x => fn => fn(x)

const data =
  Identity(4)
    .map(add)
    .ap(Identity(thrush(5)))

What is the value of making this change?

Consistency.

Before:

map   :: Functor f => f a        ~>   (a -> b) -> f b
ap    ::   Apply f => f (a -> b) ~>        f a -> f b
chain ::   Chain f => f a        ~> (a -> f b) -> f b

After:

map   :: Functor f => f a ~>   (a -> b) -> f b
ap    ::   Apply f => f a ~> f (a -> b) -> f b
chain ::   Chain f => f a ~> (a -> f b) -> f b

Yeah :corn:sistency makes a lot of sense.
I think I always thought of ap being synonymous to the A combinator, allowing me to apply a value to a function and I always thought of map as a way to apply an function to a value (no offense to map).

Are there any plans to add an algebra that will allow the application of a value to function?

Also, I may stick with the old version of the spec, is there a way to signal that I am keeping ap in the old spec, but still bring in the new hotness like traverse over sequence, etc. Or am I stuck in the old spec fully?

EDIT:
Actually if there is nothing coming up to allow a value to be applied to a function, I may stick fully with the spec, make the updates to ap and add a method in my library that behaves the way ap used to. (Now what to call that feller.)

Currently there aren't any plans to update the old spec. Mainly from lack of time on my part and inconsistencies that any new changes would bring. Also it wouldn't be fair on you if I did say "yes" and not do anything medium to long term.

Personally I think the new ap is more consistent and easier to reason about. There isn't anything stopping you from making your own ap使 that did what you wanted.

Awesome. You have all answered my questions.

Think the best route for me to take is to update ap to the new spec, and provide another function that flips the script and allows what my 鉂わ笍 desires.

That way I can still earn my sticker and get what I want. The whole have my 馃嵃 and eat it too situation.

Thanks so much for your time and hard work on this spec.
Is it cool for me to close this issue out, or do you like to do that?

There you go 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

framp picture framp  路  16Comments

davidchambers picture davidchambers  路  8Comments

paldepind picture paldepind  路  7Comments

dmitriz picture dmitriz  路  14Comments

leeoniya picture leeoniya  路  6Comments