fantasyland / fantasy-land Goto Github PK
View Code? Open in Web Editor NEWSpecification for interoperability of common algebraic structures in JavaScript
License: MIT License
Specification for interoperability of common algebraic structures in JavaScript
License: MIT License
Provides a ap
and of
methods. Monad inherits from Applicative and derives ap
as a.chain(function(f) { return b.map(f); })
.
Quick translation of the laws. Will need some checking:
a.of(function(a) { return a; }).ap(v)
is equivalent to v
(identity)a.of(function(f) { return function(g) { return function(x) { return f(g(x))}; }; }).ap(u).ap(v).ap(w)
is equivalent to u.ap(v.ap(w))
(composition)a.of(f).ap(a.of(x))
is equivalent to a.of(f(x))
(homomorphism)u.ap(a.of(y))
is equivalent to a.of(function(f) { return f(y); }).ap(u)
(interchange)It's quite possible that I'm simply missing something simple, but I'm disturbed by this sentence (emphasis added):
A value which has an Applicative must provide an
of
method on itself or itsconstructor
object.
(All the same points will apply to Monoid's empty
method, but it's Applicatives which are worrying me now.)
For a specification which is usually so prescriptive, this is surprisingly lax. But that's not the real problem. It seems to me that this makes it tremendously more difficult to write code that works across all Applicative Types; in fact, it probably makes it impossible.
A few points:
of
. Should the specification be taken to read, for instance, "The appropriate one of m.of(a).chain(f)
or m.constructor.of(a).chain(f)
is equivalent to f(a)
(left identity)" or some more precise version of the same?Object.create
, why should the specification assume that I use constructor functions to define my types? It's quite possible to do without them, and it's growing ever more popular to work that way.of
on both. This would probably not cause an issue. But nothing in the specification would prevent me from creating a conforming of
on the instances and an unrelated of
on the constructor or vice versa: Maybe.of = function(val) {return new Just(val);};
Maybe.prototype.of = function(val) {throw "Unknown Preposition";};
Does that conform to the specification as written so long as Maybe.of
upholds the required laws?
Does this one so long as Maybe.prototype.of
upholds the laws?:
Maybe.of = function(val) {throw "Unknown Preposition";};
Maybe.prototype.of = function(val) {return new Just(val);};
If so, can anyone suggest a way that I can generically apply of
to algebraic types without knowing for each specific type which version is being used?
Or... am I just missing something simple again?
This was all brought to mind by a recent Ramda issue.
In the spec it says "A value which has a bind" when defining chain. The word "bind" is not defined or generally ambigious
Is zero the "empty monoid". For example for set would it be set concatenation & the empty set?
Promises are not and probably will never be monads.
So consider using something not taken by the promise people to avoid confusion
It seems like we're implementing comands in a few of the libraries (Promises, etc) we should update the specification around this. Then we can update the quick check library to allow us to test for this.
Assuming the following:
extend extract = id
extract . extend f = f
extend f . extend g = extend (f . extend g)
Over at #68 I had an issue with using Id
to express a traversable law - i.e. using an implementation within the spec.
At #66 @phadej suggested we motivate some reason to depend on the spec so we can chart it's use and have a reason for versioning.
I'd like to see a law quickcheck test script included in the spec. Something like: https://github.com/folktale/laws
That would allow people to add fantasy-land as a dev dependency as well as give me a reason to include Id
My vision is something like:
var fl = require('fantasy-land')
fl.quickcheck(MyType, [fl.functor, fl.traversable, fl.foldable]);
I also believe it makes sense to do the derivations from fl
, but that might be overboard.
Needs to test values against the algebraic laws we specify. The best method would be a QuickCheck like framework.
It was a little confusing to see same as a param to map
and chain
. May be we sould somehow separate them (fM looks ugly, guess there'r some other options)?
Currently Applicative is the only one that is "backwards" from the other algebras, where the ap
is working on a function rather than the data. It makes this a bit hard to think of in terms of map
or chain
. Can we have it where a.ap(f)
implies that a
is some data, e.g. option, and f
is a function?
Thoughts, ideas, complaints?
Hi guys,
I've been implementing FantasyLand in monet.js and I've come a little unstuck with ap()
.
Unless I've got the wrong end of the stick FantasyLand's ap()
demands that the applicative already contains a function and then accepts an applicative with a value.
appWithFunction.ap(appWithValue)
Unfortunately I've followed scalaz & functional java approach which does the opposite.
appWithValue.ap(appWithFunction)
See here for the Maybe
example: http://cwmyers.github.io/monet.js/#apmaybefn
Here is an example from FunctionalJava Option
:
public final <B> Option<B> apply(Option<F<A,B>> of)
So my question is why has Fantasy Land taken this particular approach to applicative? I'm worried that this would be a large breaking change to the monet library and I'm trying to figure out what the right thing is to do.
Cheers,
Chris
It seems like in a language like JavaScript where currying isn't the norm (and might not be performant because of a lack of compiler help), it might be more useful to make something more like LiftN
the requirement for applicative rather than ap
. It could even be backwards compatible for non-library code by just allowing ap
to take multiple arguments like this:
MyApp.of(f).ap(a, b, c, d, e)
This would be fairly easy to implement for most applicatives I can think of, and would certainly be more convenient for the user and probably more performant, too.
If this doesn't match the aesthetics of fantasy-land (and my guess is it doesn't), maybe there should be a way for implementors to optionally provide a liftN
method (under some friendlier name, maybe just lift
) for performance reasons?
I think there is a problem with having the functor's map method being called "map". Many other libraries provide implementations of methods called map
that are inconsistent.
map
function takes a function with the signature Number -> Element -> a
map
is a -> Number -> [a] -> b
If the implementation is already there then it is really hard to fix, if it wasn't there at all then we could just add a compliant implementation with the right name. Might it be better if we called the functor's map function something else? Maybe fmap
?
I guess that would be an annoying change to make at this point. Maybe implementations could look for an fmap
method first and fall-back to map
if it doesn't exist?
There is no notion of error handling in the spec, if wish promises to ever become monadic we should define strategy for error handling.
I think promises/a+ is a lost cause. But DOM Futures is probably not. I thin it would be very useful to have spec outlined and proposed to whoever is working on DOM Futures! I think
the goals of the spec should be:
Define Monad
-ic promises that that is backwards compatible with Promise/A. In other words then
is de-facto standard and it's too late to change it. But then
could be defined by complecting flatMap
and some error handling mechanism (see #8)
I'm starting to see a bunch of projects starting up implementing some version or other of this spec. Since it's changing quite a bit at this early stage, I propose adding a semver version number to allow for clarity of which version of the spec is being implemented.
Each law should be named so it's easier to know what's going on. It's better to say "Law of Associativity" so people can figure out the equivalence.
Why is it important for the Applicative of
function to not check it's input?
My question arises because I have seen two different types of implementations for Maybe
, one that pretty much looks like this where the check to see if the value is null is done in the map function:
function Maybe(val) {
this.val = val;
};
Maybe.prototype.map = function(f) {
return this.val == null ? this : new Maybe(f(this.val));
};
and another one which has 2 sub types Just
and Nothing
.
function Maybe(val) {
return this.val == null ? new Nothing() : new Just(val);
};
function Just(val) {
this.val = val;
};
Just.prototype.map = function(f) {
return new Maybe(f(this.val));
};
function Nothing() {};
Nothing.prototype.map = function(f) {
return this;
};
My point here is that the second implementation does a check in the constructor, and because of this it would break the fantasy land specification if it implemented of = function(val) return Maybe(val)
but this is not true for the first implementation since it does the checking in the map method.
In the second case, Maybe.of
can never return something that will not apply the input function in map.
This leads to the situation that because of internal design decisions Maybe.of
may be forced to be inconsistent with other implementations in order to fulfill this specification. I find this strange and wonder if I have misunderstood something.
Use concat
as the method name. Makes Array
compatible.
I did like the name extract
that was proposed in #57 much more. Why was from
chosen?
To me (and probably most other people who don't know what comonads are about), extract
is more descriptive and meaningful. Don't we want a self-explanatory spec? It matches the Haskell function as well.
Also, I have some reservations against using from
. The term is used in ES6 for static (constructor) methods to assimilate values in a type, e.g. Array.from
. Don't we expect some collisions, especially since from
originally had a static counterpart (7253b83)?
Sorry for not bringing this up earlier, I seem to have forgotten to watch this repo.
In id.js:
// Comonad
Id.prototype.from = function(a){
return a.value;
}
However according to the Comonad spec:
The from method takes no arguments
I propose to change the spec so it would require more unique method names. For example, instead of ap
method, a @@fantasy-land/ap
. It can be called as foo['@@fantasy-land/ap']()
.
This would serve two purposes:
This also will allow to create a polyfill that adds fantasy-land support to native JS data structures. It relatively ok to add @@fantasy-land/ap
method to Array.prototype
, but less ok to add ap
.
Also libraries like Bacon, for example, will be able to add fantasy-land support, as for now it problematic with map method, for instance, as it not strictly compatible with the spec, but they could add @@fantasy-land/map
that is.
I think this change could significantly speed up the adoption of fantasy-land specification.
The idea inspired by transducers protocol, seems like it worked out for them pretty well.
See also discussion on Gitter: https://gitter.im/fantasyland/fantasy-land?at=553cb82d20328f114ca36f0f
.map
and .chain
are cool for writing higher order functions over things.
Another primitive I use a lot is .filter
and I don't know how to implement in terms of .map
/ .ap
/ .chain
.
Is there an algebra for it?
Suggestion: Give a short tutorial on README.MD
on how to install and use libraries. Or create a single standalone file.
This is a dumb question I'm sure - so thanks in advance. But how am I supposed to go about and use these libraries? I see they use requirejs so I downloaded that library and learned how to use it.
I then downloaded the fantasyland
libraries I needed using bower.js. I set up the require config file using bower-requirejs library.
I expected that I would do something like this:
define(['lodash', 'bilby', 'fantasy-lenses'], function(_, b, fantasy){
//My code
}
Which give the error:
Uncaught Error: Module name "daggy" has not been loaded yet for context: _.
So I go through and add all the libraries and it gives me the error:
Uncaught TypeError: Cannot read property 'Lens' of undefined
of(a).then(f)
equalsf(a)
m.then(of)
equalsm
What is of
here ? Should not it be part of some constructor ?
Traversable is pretty neat. What do we say about adding this to the spec? Of course it depends on #7 being approved. Is there standard nomenclature for either traverse
or sequence
? Which one are we suggesting in the spec? Or do we allow either, with a derivable implementation for the other?
Whether to support this project (fantasy-*) in the future and maintenance npm packages?
I'm afraid it's not always correct to assume that a Monad is also an Applicative. At least I tried to implement the specs into Bacon.js today, for an excersise, and run into issues because of this assumption. I'm not sure how familiar you're with Bacon.js, but I'll try to explain the case anyway.
Now an EventStream in Bacon.js is clearly a Monadic structure: the flatMap
method (aliased now as chain
) of EventStream has one parameter which is a function that creates a new EventStream for each value in the original stream. In combination with EventStream.of
we have a nice Monad. Except.
The Applicative interface for EventStreams doesn't make much sense, at least if based on flatMap: the result stream a.ap(b)
would take apply each function from the source stream to all (or one?) subsequent elements in the stream b. It would make more sense to base ap
on combine
or even zip
, but I'd rather not have EventStream implement Applicative because of this ambiguity.
In Haskell, you can declare Monad without Applicative. Why not in Fantasy Land?
The fantasy-land version of Bacon.js is in the fantasy-land branch. I'm planning to bring that to master, if I can come up with a nice solution. The current EventStream/Applicative one seems to be compliant, but I would never use it because Applicative through flatMap just makes no sense.
I'd like to have EventStream implement Functor and Monad (no Applicative), and Property implement Functor and Applicative (and a Duck Monad (Property.flatMap returns an instance of EventStream)).
Please ask for more details if this made no sense to you :)
Sourcing from a when.js issue:
p.chain(f).chain(g);
p.chain(h);
In the presence of side-effects, the order of execution matters. If the order is FHG (i.e. breadth-first), and h has a side-effect, then g may be affected by a totally separate chain (and indeed, p.chain(h)
could be in an entirely different part of the file).
We could stick with precedent and push the concern downstream, but I want to at least raise the issue as something that should be considered somewhere along the line.
What is the reasoning behind the choice of identifiers u
, v
, w
, x
, and y
in the specification? I can see why a
, b
, f
, and m
were chosen in the contexts they're used, but I can't do the same for the letters near the end of the alphabet.
It should be clear what license the documentation and test suite are under, and perhaps a CONTRIBUTING
file that makes clear that pull requests are implicit license. This latter is a bit litigious but I've seen it around sometimes.
I did my best to implement the spec, but i have never read category theory or haskell. So if someone could just take a look and see if laws are broken...
Without any particular background in Haskell or any other pure functional algebraic approach, but with an inkling that this approach might be super useful in a wide variety of situations, I've attempted to create a minimal implementation of the structures described in this spec:
Consider it a null implementation, if you will. I'd love to see examples of generic functions which can operate without error (even if semantically nonsensically) on this minimal structure.
I tried to implement the spec as closely as possible in the tests.
Why complicate things with constructor
. Let's just have every monad have two properties.
Back compat with promises for constructor.of
may be a bad idea if we're going to rename then
anyway.
We can also use monad.point
if we prefer.
As mentioned here I'd like to propose a Cofoldable
. There's detailed information about it here
The idea is that while Foldable
and Cofoldable
are lawless alone, together you can suggest some laws on them.
It should have one of two methods:
fromArray :: [a] -> f a
unfold
I'm actually unsure on the definition of unfold
Should it be (a -> b -> b) -> b -> b -> f a
as in the definition for `build in the link, or something else? In any case, one can be derived from the other.
Please discuss.
For example:
Id.prototype.then = function (m) {
return m;
}
Promise.prototype.then = function (m) {
var promise = this;
return new Promise (function (resolve) {
return promise.fork (function (dontCare) {
return m.fork (resolve);
});
});
}
Then you could have code like this:
somethingToDo (x) // Returns Future
.chain (thisAlsoReturnsAFuture)
.then (justDoFinalFuture)
instead of:
somethingToDo (x) // Returns Future
.chain (thisAlsoReturnsAFuture)
.chain (konst (justDoFinalFuture))
Thoughts?
I was reading through http://book.realworldhaskell.org/read/error-handling.html and trying to see how it would be adapted to Fantasy Land js, but struggling a little bit. I would really appreciate if anyone could come up with some example code to deal with this situation:
Assume some Fantasy Land compliant promises implementation:
var err = Promise.reject(new Error('broken'))
var ok = Promise.of(10)
var sum = liftA2(err, ok, function (a, b) {
// this should never be called
return a + b
})
return sum
// later, in some other scope, where I only have access to sum:
sum.chain(function (sum) {
// this should also never be called
})
// how do I get the error message?
https://github.com/leoasis/fnky
I implemented a library for functional programming Javascript, with the common constructs like functor, applicative, monad and monoid, following the Fantasy Land spec.
It also includes some common implementations, like Maybe and Either.
I'd appreciate any suggestions from you guys, and things you may see that break the spec in any way. I'd like your approval to claim that it follows the spec (and put the badge there).
Obviously it's not complete, but I plan to keep building on it.
is
a.concat(b).concat(c)
is equivalent to a.concat(b.concat(c))
the same as
a.concat(b).concat(c)
is equivalent to b.concat(c).concat(a)
I would find the second definition more intuitively readable about what the law means
I feel like I am entering the depths of a seldom visited rocky recess. Hello.
I have been diving into functional paradigms, increasingly, for at least a year. I am interested in and actively trying to author my javascript-based work with a functional hand.
Given this, I felt quite lucky to fall upon this [Fantasy Land] project given it appears to be helping translate ideas from purer languages into javascript.
Language-wise I have focused my functional learning efforts to date on Haskell
, because it/its community appears to tackle the matter rigorously and have pioneered certain or many functional programming breakthroughs.
I was surprised to find however that fantasy-land terminology differs from haskell's insofar as option
instead of maybe
, and moands wherein I know they require a return
(sometimes called unit
) and bind
method but I've only seen of
and chain
mentioned in Fantasy Land (Still not sure but I'm assuming of
=== return
and chain
=== bind
?).
Is this spec going against conventional functional naming or is it Haskell that deviates?
Wikipedia describes semigroups as a set that has a binary relation. You should probably come up with something a little bit more concrete and grounded though.
Spec tends to refer to equality, which is not defined:
u.map(function(a) { return a; })) equal `u`
What kind of equality are we talking about ==
or ===
or likely something else. If not later than array is no longer a functor.
Every algebraic data type should contain motivation for WHY it's a useful abstraction.
It took me a while to understand why any of these are useful abstractions.
This is really for my own curiosity and learning, but I'm wondering why the spec doesn't explicitly call for a "join" operation for monads?
On one hand, I can see how it's implicit in chain
. On the other, it seems like many (most? all?) times, chain
can be defined in terms of map
and join
. Perhaps it's because chain
is the more often used operation, thus making it a "better" public API to specify, and join
is typically a means to an end? (the end being to implement chain
)
Thanks for helping me understand!
In ES5 Array.of
does not exist. It does in ES6 though.
Use reduceRight
as the method name. Makes Array
compatible.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.