fantasy-land: `ap`is not reasonable
I am writing this issue to prevent confusion to new readers.
With the change of the ap
function, ‘pretty’ is favored over readability.
definition of map:
A linear transformation; i.e. a -> fn(a)
definition of chain:
A derive of a composition, i.e. g(fn(a)) -> g(a).f(a)
definition of apply:
Isomorphism of a left-associative function, i.e. fn(x*y) -> fn(x,y)
The ap definition as of right now suggests right-associative, giving the reverse of what an apply does.
We may enjoy having consistent definitions, but please do not spread false definitions.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 39 (28 by maintainers)
I think we should close this, although this is a good discussion, it’s not going anywhere… If anyone disagrees just reopen…
@davidchambers and @joneshf: well thinking on the feedback you two have given me, it seems like it all goes back to what my dear, sainted grandaddy Hicks used to say: “Ian, always remember, Union Types make everything better”
As an aside, your data type you posted up there is not a
Functor
: https://github.com/fantasyland/fantasy-land/issues/85#issuecomment-73419882Just a thought, @evilsoft: you’re free to define
ap
any way you please now that we have prefixed names. If you intend users of your ADT to use the object-oriented interface directly you could provide anap
method which works as you prefer (in addition to providing a compliantfantasy-land/ap
method).Your Maybe type is invalid, @evilsoft. It doesn’t permit
Just(null)
andJust(undefined)
.The pay-off is getting to write parametrically polymorphic functions such as this:
Users of FL-compatible data types benefit from programming against a standard, principled interface.
Why use
ap
rather thanmap
? This could simply bemap(add(3))
, I believe. I may not be the best person to comment on this, though, as I like to useR
/S
/Z
functions rather than invoke Fantasy Land methods directly.@davidchambers IMO, that implementation that you provided for
Maybe.prototype.ap
seems a little odd. I typically do mine more like this:Trying to avoid doing checking and what have you on the foreign types and just letting the algebras take care of it for me. Notice not ONCE do I extract or otherwise do anything with the foreign Apply, I just
map
it and letmap
take care of it.And I am curious about what you mean by the users of FL. I thought the you needed ADT lib authors to implement against the spec in order to get users to even be aware of FL? Who are the users that FL is targeting then? How would someone not using some ADT lib use FL?
I know I would not want users of my libs losing the ability to do something like (totally contrived, but I hope it makes sense. There are like 100 different, better ways to do this, just using a simple example):
With the new bit, end users are forced into a more point-wise flow:
(again i KNOW it would be better to just do
x => Maybe(add(3)).ap(Maybe(x))
just trying to keep it easy to follow.)I mean I am just a noob at this stuff, but I think a point-free composition is a little easier to follow from an end-user, non-ADT lib developing developer perspective. So it seems to be a PITA from either user.
And granted, a majority of the time, my users will be using my liftAN functions that will hide the nasty and allow them to still be point-free. But, it is just terrible if they decide that they need to
ap
bits themselves.From an ADT lib dev perspective, I am forced to extract and do operations on a foreign Apply From an end-user perspective, I loose the ability to choose between point-wise or point-free implementation and am forced into a point-wise interface.
I know you guys will not change it, because you have some good reasons not to. Please do not take this as a foolish attempt at rabble rousing. I am just voicing my pain points as I try to navigate through this change. It is too bad I do not use T$, that way I could get some benefit from all of this. 😉
I feel sorry mostly for those end users who are going to go through the 9 planes of heck as interop between the current libs is going to be super chaotic while people make the transition. Some will be 0.x and others will not for extended periods of time, if people even decide to transition. So for a while we will lose all kinds of interop and could not safely upgrade until ALL FL deps in a project are upgraded. And even then all
ap
code will have to be seriously reworked from the end user perspective. Going to be a bumpy transition indeed.Specs are hard 😸
Argument order is actually irrelevant:
If the value of type
Maybe (a -> b)
isNothing
there is—as you rightly stated—no function to apply, so the result isNothing
. This should not result in an exception. Perhaps you’re using a data type with the newap
in conjunction with Ramda, which is not yet compatible withfantasy-land@1.x.x
.I also think that this order of arguments for .ap is not ideal. Not just it less intuititve, it also may cause problems, I just ran into one. Trying to use a version of traverse (like the one that ramda implements), with .ap implemente by the spec if, somehow the code reachs a point where the value (function) of something like Maybe.Nothing (which has no value) should be applied to the value of another structure, the code breaks because, well there is no function to be applied. The older version of .ap didn’t have this sort of problem
@evilsoft, if you’re interested in run-time type checking have a look at sanctuary-js/sanctuary#216. Here are some
S.ap
usage examples:Just want to clarify that Fantasy Land doesn’t take inspiration in any JavaScript specifications. The purpose of this spec is rather to specify how to apply research done in languages like Haskell to JavaScript. So the
ap
method has nothing to do really withFunction.prototype.apply
.