Comments (23)
appWithValue.ap(appWithFunction)
looks really odd to me. Applying functions with multiple arguments doesn't seem to work well - or is quite ugly, if we look the linked example:
var person = function(forename, surname, address){…}.curry() var maybePersonResult = maybeAddress .ap(maybeSurname .ap(maybeForename .map(person)))
Not only is there much nesting, but also the order of the arguments is inverted. Having used .map()
instead of .ap(Maybe.of())
might save characters and an explicit Maybe
reference, but doesn't make it look better.
The appWithFunction.ap(appWithValue)
order is better imho:
Maybe.of(person).ap(maybeForename).ap(maybeSurename).ap(maybeAddress)
// or even
Maybe.of(person).ap(maybeForename, maybeSurename, maybeAddress)
It wouldn't be more consistent to flip it just because we have flipped fmap
to become a .map
method of a Functor instance - Applicatives are more than Functors and .ap
can already be a method of an Applicative instance.
from fantasy-land.
I guess I copied Haskell's <*>
infix operator. Just like <$>
(i.e. infix fmap) - it's flipped to what it probably should be.
It'd probably be more consistent to flip it (i.e. it would look more like other functors).
What should we do about updating the specification? I have no hesitation to release new versions of my projects but does anyone have a project where they can't or don't want to change the order of ap
?
from fantasy-land.
I think this goes back to #24. However, we haven't really been keeping up with updating the version number to begin with. If you'd like I can go back to when version 0.0.1 was first "released" and look through the changes since then, tagging and whatever. Or we can just start right now with a set version, and keep everything semver'd from here on.
from fantasy-land.
There's an alternative definition of Applicatives that uses a "join" or "seq" operator instead of "ap".
Here's how join
(unrelated to join :: m (m a) -> m a
) would look (note: code in this comment is pseudo-haskell):
class Functor f => Joinable f where
pure :: a -> f a
join :: f a -> f b -> f (a, b)
It's equivalent to ap
:
join u v = ap (map (,) u) v
ap u v = map (\(f, x) -> f x) (join u v)
And it even comes with equivalent laws:
join (pure x) v = map (x,) v
join v (pure x) = map (,y) v
join (map f u) (map g v) = map (f *** g) (join u v)
join u (join v w) = map (\(x, (y, z)) -> ((x, y), z)) (join (join u v) w)
map f (pure x) = point (f x)
The "ap"-based one seems to be the best in Haskell since functions are auto-curried and tuples are non-recursive. But in JS we can use heterogenous lists instead, so "join" might be more natural to express. In fantasyland, I guess it would be something like "a.join(functors)" is a functor of a combined array of results from the given functors.
Anyways, slightly rambly, but this represents the way the seq
method is used in Parsimmon, which is much more naturally usable than ap
(in fact, Parsimmon's ap
is implemented in terms of seq
). It'd be worth considering whether to standardize this sort of interface, since I can imagine it coming up quite a lot.
from fantasy-land.
related to #56, although liftN
still requires a function.
from fantasy-land.
In Parsimmon I've recently gone with the join
implementation I mentioned here, although I renamed it to seq
to match Parsnip. Basically, seq :: [Parser *a] -> Parser [*a]
where *a
is heterogenous. ap
is implemented in terms of this primitive.
from fantasy-land.
Yeah, this needs to be fixed. PR would be awesome!
from fantasy-land.
Should we flip the order of ap
, or just live with it?
from fantasy-land.
I'm having trouble typing ap()
with Flow, ended up with this:
class Stream<T> {
// Apply
ap<A,B>( other:Stream<A> ): Stream<B> {
// In order for .ap() to work `this` must be a `Stream<A => B>`,
// but I don't know how to express that constraint in Flow, so just use `any`
const streamF:BasicStream<any> = this.observe
return new Stream(bs.ap(streamF)(other.observe))
}
}
Would be straightforward if it was other way around.
from fantasy-land.
That is quite unfortunate. Can you actually write the other way with flow?
from fantasy-land.
Sure, with reversed version of ap it will look like this:
class Stream<T> {
ap<A>( other:Stream<( x:T ) => A> ): Stream<A> {
return new Stream(bs.ap(other.observe)(this.observe))
}
}
from fantasy-land.
For the record: not a big problem for me, just wanted to add my two cents :)
from fantasy-land.
I've been hit with this recently as well. So the question is how do we change it, because I think we should!
Could we maybe rename ap
to something else, or do we say if you depend on the new fantasy-land #92 ap
works like this now?
from fantasy-land.
We could certainly use NPM to help us manage changes like that after libraries start depending on FL.
We could do for example:
1.0
backward compatibility release with unprefixed names and no changes to the spec2.0
adds prefixes therefore providing clashes safety, but still no changes to the spec3.0
no changes in method names, but includes changes to spec like this and #88
Then libraries could specify appropriate versions range in their peerDependency to fantasy-land
, and consumers won't be able to use two incompatible libraries (they won't be able to install version of fantasy-land
that satisfies peerDependency for all of incompatible libs).
from fantasy-land.
I'd rather have 3.0 out before 2.0 💃
from fantasy-land.
No preference on that matter from me :)
We could also squash 3.0 and 2.0 to one version, btw.
from fantasy-land.
So you only want to change this because flow's type system is not sophisticatd enough?
from fantasy-land.
Yep, and also the fact that it's inconsistent with map
and chain
and was a mistake from the beginning (see #50 (comment))
Feel free to discuss in the PR: #145
from fantasy-land.
I think this can be closed now, no?
from fantasy-land.
Indeed it can!
from fantasy-land.
Sorry for commenting on closed issue, but It may help somebody dealing with the same issue as I did. I heavily use monet.js algebraic structures along with ramda. As @cwmyers pointed out, applicatives in monet.js are not compatible with fantasy-land spec. Though one cannot use ramda's liftN
to work with monet's applicates. I implemented liftFN
to create interop for monet.js and ramda. It is part of ramda-adjunct and you can find more information in the docs.
from fantasy-land.
@char0n looks like ramda#1900 was merged. So soon, very soon, once ramda releases its next version we should all be gtg!!
from fantasy-land.
@evilsoft I've just tried using lift
from master branch of ramda, but it still doesn't work with monet.js algebraic structures. liftFN from ramda-adjunct still working. I am still investigating the issue...
I have one additional question regarding composition of Apply spec regarding v.ap(u.ap(a.map(f => g => x => f(g(x))))) is equivalent to v.ap(u).ap(a) (composition)
const add3 = curry((a, b, c) => a + b + c);
const m10 = Maybe.Some(10);
const m15 = Maybe.Some(15);
const m17 = Maybe.Some(17);
m10.ap(m15.ap(m17.map(add3)))
What is the eqvivalent of v.ap(u).ap(a)
in my real code example ? Is this type of composition still possible when the ap
argument must be apply of a function ?
Update:
monet.js PR #112 makes monet.js compatible with fantasy-land spec and next version of ramda (>0.23.0). Use [ramda-adjunct](https://github.com/char0n/ramda-adjunct} and liftFN
for time being.
from fantasy-land.
Related Issues (20)
- [Question] Type signature for ap HOT 2
- Adding Folktale to implementations.md
- Equivalent for Serial typeclass? HOT 1
- Is really necessary specify "No parts of <...> value should be checked"? HOT 11
- Add Traversable1 HOT 3
- Help understanding use of identity in Traversable laws HOT 2
- Fantasy Land specification version number HOT 1
- Switch to Symbols HOT 2
- Wrong argument in Traversable HOT 4
- Is monet.js compatible? HOT 1
- Why FL specifies the ChainRec typeclass when there is the trampoline monad? HOT 7
- What does "and" mean? HOT 2
- Can someone explain me how I should understand Semigroup? HOT 5
- Remove all OOP features HOT 21
- Traversable and Foldable use multiple variables as arguments HOT 12
- Profunctor should require Contravariant HOT 10
- Implementation of `Compose` is confusing HOT 3
- should Chain properties depends on fantasy-land/ap (and possibly .map)? HOT 4
- Why putting "fantasy-land/" before everything in readme? HOT 3
- Is `Pair` a valid instance of `MonadRec`?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from fantasy-land.