Closed jlmorgan closed 7 years ago
First, you shouldn't expect to be able to compare your values with ===
. You'll need to define a fantasy-land/equals
method. Secondly, y.ap(y)
is correct (I think) but quite confusing. One can provide identity
as an argument to identity
(identity(identity)
) but I think it wise to start with more straightforward applications such as Math.sqrt(9)
. For example:
'use strict';
var Z = require('sanctuary-type-classes');
function Thing(value) {
if (!(this instanceof Thing)) return new Thing(value);
this.value = value;
}
Thing.prototype.equals =
Thing.prototype['fantasy-land/equals'] = function(other) {
return Z.equals(this.value, other.value);
};
Thing.prototype.ap =
Thing.prototype['fantasy-land/ap'] = function(other) {
return Thing(other.value(this.value));
};
Thing.prototype.inspect =
Thing.prototype.toString = function() {
return 'Thing(' + Z.toString(this.value) + ')';
};
Thing(9).ap(Thing(Math.sqrt));
// => Thing(3)
Z.ap(Thing(Math.sqrt), Thing(9));
// => Thing(3)
I hope this proves helpful. :)
also read about Comparison operators in JS
My presumption on how .ap
was suppose to work and what the test implied was that if the final value would ultimately just be the original identity function being passed along. ~Your correction to the implementation of .ap
alters that perception as now the composition has more of an affect on the computation resulting in a differing function reference.~ My not using left.value
and right.value
in the check is what made the left === right
not work out as they are obviously new instances thanks to .map
.
const y = new Thing(x => x);
const left = y.ap(y.ap(y.map(f => g => x => f(g(x)))));
const right = y.ap(y).ap(y)
Something that would help immensely when learning this material is not having to learn Haskell in order to read JavaScript. Function signatures actually in JavaScript would help greatly. I understand that JS doesn't have types, but those things can be inferred through annotations or jsdoc stuffs.
On a side note, thanks to all for providing this reference project. = D
So, for a brief moment on here, I saw:
ap(other) { return this.map(other.value); }
and above he has:
ap(other) { return Thing(other.value(this.value)); }
which is it?
you can fix your original ap
this way too:
- ap(other) { return other.map(this.value); }
+ ap(other) { return this.map(other.value); }
and if you inline definition of map
you get:
ap(other) { return other.value(this.value); }
Function signatures actually in JavaScript would help greatly. I understand that JS doesn't have types, but those things can be inferred through annotations or jsdoc stuffs.
sanctuary-def, unlike TypeScript, can accurately express the type of ap
. It's pretty noisy, though! I think learning to read Haskell type signatures is a better use of time than learning how sanctuary-def works. ;)
Haskell:
ap :: Apply f => f (a -> b) -> f a -> f b
JavaScript:
// a :: Type
const a = $.TypeVariable('a');
// b :: Type
const b = $.TypeVariable('b');
// f :: Type -> Type
const f = $.UnaryTypeVariable('f');
// ap :: Apply f => f (a -> b) -> f a -> f b
const ap = def('ap', {f: [Z.Apply]}, [f($.Function([a, b])), f(a), f(b)], Z.ap);
So, does this mean that the intention of ap
is to apply boxed functions to a boxed value? In a sense where [Apply]
is a box, add1
increments, and mul2
multiplies by 2:
[Apply 1].ap([Apply add1]).ap([Apply mul2]) -> [Apply 4]
You may find the examples for Z.ap
instructive, @jlmorgan. I find Haskell examples clearer, though:
λ [(*10),(*100),(*1000)] <*> [1,2,3]
[10,20,30,100,200,300,1000,2000,3000]
<*>
is the name for ap
in Haskell. It takes the "thing" of functions as its left operand and the "thing" of arguments as its right operand.
ap
exists in Prelude as well.
λ: ap [(*10),(*100),(*1000)] [1,2,3]
[10,20,30,100,200,300,1000,2000,3000]
I didn't know that, Hardy. Thanks for the tip. :)
I'll close this issue as there's nothing to fix or change, but feel free to keep commenting.
:+1:
On Mon, 12 Dec 2016, 21:35 David Chambers, notifications@github.com wrote:
I didn't know that, Hardy. Thanks for the tip. :)
I'll close this issue as there's nothing to fix or change, but feel free to keep commenting.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/fantasyland/fantasy-land/issues/209#issuecomment-266560064, or mute the thread https://github.com/notifications/unsubscribe-auth/ACcaGMkstrXtgSd7feV2oSLN8yKXupQ_ks5rHb4sgaJpZM4LKop0 .
When I read the above code with the mindset of function first, it make me think the following should occur:
const mul = left => right => left * right;
Thing(mul(100)).ap(Thing(2));
// => [Thing 200]
And not how it seems implemented above which is more like:
Thing(2).ap(Thing(mul(100));
// => [Thing 200]
Thinking function-first, data-last would seem to suggest that the function would be boxed first, then applying values to that function. Similar to Function#apply
or a curried Function#call
. Also, where's a good place to have interactive conversations with people that know both sides of this well enough? I have other questions and don't want to drag on this "issue" too much.
Similar to
Function#apply
or a curriedFunction#call
.
Don't contaminate your thoughts with this sort of comparison. ;)
I suggest comparing ap
to map
. They're very similar:
> Thing(9).map(Math.sqrt)
Thing(3)
> Thing(9).ap(Thing(Math.sqrt))
Thing(3)
The type signatures are instructive:
map :: Functor f => (a -> b) -> f a -> f b
ap :: Apply f => f (a -> b) -> f a -> f b
Also, where's a good place to have interactive conversations with people that know both sides of this well enough? I have other questions and don't want to drag on this "issue" too much.
As @davidchambers mentioned, please feel free to continue the discussion in this issue if you'd like. If you want more real-time communication you can join the gitter channel: https://gitter.im/fantasyland/fantasy-land Though, it's longer to get a conversation started there :\
I didn't know that, Hardy. Thanks for the tip. :)
I think that's actually where ap
comes from. :)
@joneshf <*>
== ap
and also ap
doesn't exists in Prelude, ap
contains in Control.Monad
It could be that my implementation is not correct, but here's the result of the test.
test.js
Running test.js