New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Profunctor specification #124
Conversation
|
||
const composition = t => eq => x => { | ||
const a = p[dimap](compose(identity)(identity), compose(identity)(identity)); | ||
const b = p[dimap](identity, identity)[dimap](identity, identity); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these lines use t(x)[dimap]
rather than p[dimap]
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, yes they should.
On Fri, 29 Jan 2016, 22:22 Scott Christopher notifications@github.com
wrote:
In laws/profunctor.js
#124 (comment)
:+### Profunctor
+
+1.p.dimap(f, g)
is equivalent top
(identity)
+2.p.dimap(compose(f1)(f2)), compose(g1)(g2))
is equivalent top.dimap(f1, g1).dimap(f2, g2)
(composition)
+
+**/
+
+const identityʹ = t => eq => x => {
- const a = t(x)[dimap](identity, identity);
- const b = t(x);
- return eq(a, b);
+};
+const composition = t => eq => x => {
- const a = p[dimap](compose%28identity%29%28identity%29, compose%28identity%29%28identity%29);
- const b = p[dimap](identity, identity)[dimap](identity, identity);
Should these lines use t(x)[dimap] rather than p[dimap] here?
—
Reply to this email directly or view it on GitHub
https://github.com/fantasyland/fantasy-land/pull/124/files#r51324452.
This is great (and quite timely too, as I've recently been writing a JS implementation of the purescript-profunctor-lenses library). Is it worth including some information about deriving |
I was wondering that myself, although it's normally sufficient to say if On Fri, 29 Jan 2016, 22:36 Scott Christopher notifications@github.com
|
What about going the other way?, derive |
Or could/should |
I've also included @joneshf The issue I've got with Consider what Haskell says:
I just find it a bit confusing, considering that the rest of the spec is just a case (minus Alternatively maybe I can make a note that x.prototype.lmap = function(f) {
return this.dimap(f, identity);
};
x.prototype.rmap = function(g) {
return this.dimap(identity, g);
}; |
@scott-christopher that's an idea, could solve ambiguity? So x.prototype.dimap = function(f, g) {
return lmap(f).map(g);
} (sorry i'm just thinking out aloud here). |
@joneshf I've updated to the specification, what do you think about this one? |
Included specifications for `lmap`
913e559
to
e4004e7
Compare
After thinking about it for a while. I think the original version is more inline with the rest of the spec. Take What I think we should do, is stick with These names bring up a good point. It's going to be pretty hard to explain the difference between Also, |
functor on its first type parameter and functor on its second type parameter. | ||
|
||
1. `p.dimap(f, g)` is equivalent to `p` (identity) | ||
2. `p.dimap(compose(f1)(f2)), compose(g1)(g2))` is equivalent to `p.dimap(f1, g1).dimap(f2, g2)` (composition) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too many bracket?
p.dimap(compose(f1)(f2)),
^
(same thing at bimap
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also in the rest of the spec function composition is just 'hardcoded', instead of using a helper compose
function.
- compose(f1)(f2)
+ x => f1(f2(x))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking closer I see a whole bunch of mistakes and inconsistencies in the readme changes. I guess we'll get to them once the spec is decided upon. Sorry for the clutter. :)
Here's the two cents from the perspective of one such person; me.
This made it immediately clear to me what Profunctor is all about. I like this idea. Plus that it adds a lot of clarity to the
I wouldn't like writing abstractions over Profunctor if I'd have to check every time whether it's a Profunctor with Maybe it clarifies things if there would be a separate type for |
I'm not much of an algebraic type, myself, but something like Difunctor sounds like a sensical name to me. It sounds cooler than Lmappable, which I think is really important to mathematicians. |
@scott-christopher, @SimonRichardson: After reading the whole issue again, this quoted seciton stands out as though I hadn't read the replies above it. That was some very poor wording. I meant to agree with what you said, and provide an additional example. Sorry if I came off as ignoring what you'd written above |
How about:
I think that makes a pretty clear spec in which the naming seems logical. I'm throwing in ideas because I'd like to implement this in Fluture according to the spec from the get-go. :) I don't understand the differences between |
I'm happy with that, but don't have any time to make the changes ATM (life On Sun, 20 Mar 2016, 18:48 Aldwin Vlasblom, notifications@github.com
|
Can somebody explain to me what the difference between dimap and bimap is? Here's some of my confusions:
|
@Avaq: It's probably simplest to explain with the signatures to show how the type parameters are swapped: bimap :: (a -> b) -> (c -> d) -> Bifunctor a c -> Bifunctor b d
dimap :: (a -> b) -> (c -> d) -> Profunctor b c -> Profunctor a d We can implement bimap :: (a -> b) -> (c - > d) -> Future a c -> Future b d Try to think about how you would take a function Now consider something that represents a function, class Fn {
//:: (b -> c) -> Fn b c
constructor(f) { this.f = f }
//:: (a -> b) -> (c -> d) -> Fn a d
dimap(a2bFn, c2dFn) {
return new Fn(compose(c2dFn, compose(this.f, a2bFn)))
}
} But now there is no way to represent I'm not 100% sure about the exact meaning of covariance / contravariance in this context, though I believe it relates to contravariant functors rather than specifically to the notion of sub-typing as in other languages. My intuition around this is just that a contravariant type parameter is used to represent a function argument stating something the type expects to receive, while a covariant type parameter represents something the type can produce. For example, you might have a class Predicate {
// (a -> Boolean) -> Predicate a
constructor(p) { this.run = p }
// (b -> a) -> Predicate b
lmap(f) {
return new Predicate(compose(this.run, f))
}
} |
Seeing the signatures did indeed clear things up. The rest of your post helped me understand how the seemingly strange I've misinterpreted const Future = fork => ({
fork,
map: f => Future((l, r) => fork(l, compose(r, f))),
lmap: f => Future((l, r) => fork(compose(l, f), r)) //<- misinterpreted
}) If the above were the case, then deriving The fact that I couldn't make out this information from reading the draft spec might indicate that the spec is "incomplete" (or at the very least unclear), or it might just be indicative of my incompetence. ;) |
I think I've come to the conclusion that we should just use I'm also of the opinion now that we should perhaps not mention My earlier point about a profunctor simply being a functor with If we can reach an agreement on the spec, I'd be happy to make any amendments in a separate PR if @SimonRichardson doesn't have the time available. |
@scott-christopher Please do, I'm super busy with work atm, so I trust you to do the right thing :) I'll happily look over for a code review though. |
I have raised #137 with the tweaks suggested above. |
Closing this, as it was superseded with #137 |
No description provided.