-
Notifications
You must be signed in to change notification settings - Fork 378
Add ChainRec specification #152
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
Conversation
This is great! And the other issue was awesome, though I didn't keep up. I'm a little confused about that type signature. Where does the |
e.g.` // f :: (a -> Either a b) -> (b -> Either a b) -> Number -> MonadRec (Either Number String)
var f = Left => Right => n => n > 0 ? MonadRec(Left(n - 1)) : MonadRec(Right("Gotta get low, low, low")) The earlier function must "contain" a partial function from |
|
||
A value that implements the ChainRec specification must also implement the Chain specification. | ||
|
||
1. `T.chainRec((done, next, value) => g(done(value)), i) `is equivalent to `g(i)` |
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.
There is something wrong with g
. It's certainly takes different types in g(done(value))
and g(i)
invocations.
Maybe you meant T.chainRec((done, next, value) => T.of(done(g(value))), i)
?
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.
g
takes any type it's like T.of
but I did not used T.of
as Chain
does not requite the method of
.
On next line there is:
in case
g
returns values of typeT
i.e. this are equivalent:
Identity.chainRec((done, next, value) => Identity.of(done(value)), 1)
≡ Identity.of(1)
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.
Ah, right. It makes sense if we use of
as g
. Sorry.
Still curious if we could add more laws. Are there any laws for it in PureScript? If not, should we made up our own? Maybe we should add dependency on Monad after all, so we could use of
and map
in laws?
I just worry what is the value of having a method in the spec if we don't have laws that strictly restrict what it can do?
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.
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.
Still something bugs me about type of g
. It seems like for any law in the spec where we have a function like that we can say "for any g
of certain type". For example:
u.map(x => f(g(x)))
is equivalent tou.map(g).map(f)
for any:
u :: f a
g :: a -> b
f :: b -> c
a.of(f).ap(a.of(x))
is equivalent toa.of(f(x))
for any:
x :: a
f :: a -> b
m.chain(f).chain(g)
is equivalent tom.chain(x => f(x).chain(g))
for any:
m :: m a
f :: a -> m b
g :: b -> m c
But here we can't say something like that about g
because we can't identify it's type. I can't quite articulate why this is a problem, but it feels wrong.
Not sure, but it feels like we can only say "there exists g
so that ..." instead of "for any g
...", in which case it's very loose restriction.
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.
That's right. The first law says these two should give equal results, and the second says chainRec f
should use no more stack (asymptotically) than used by f
.
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.
@rpominov type of g
is a => T a
. maybe I have not clearly expressed that with words. it looks like there is no word on f/g
, for example in monad spec . should I remove the line about g
? or what you think is best wording for that?
What you think on something like this?
g
returns value of typeT
which contains value of it's first argument type.
About the other laws we could state this
T.chainRec((next,done,v) => p(v) ? f(v).map(done) : g(v).map(next), i)
is equivalent to(function step(v) { return p(v) ? f(v) : g(v).chain(step) }(i))But stack usage of
T.chainRec(f,i)
must be at most a constant multiple of the stack usage off
itself.
And types would be:
p :: a -> bool
g :: a -> T b
f :: a -> T a
step :: a -> T b
And we could also include this in laws
like this:
const someNameHere = t => eq => p => f => g => x => {
const a = t.chainRec((next,done,v) => p(v) ? f(v).map(done) : g(v).map(next), x);
const b = (function step(v) { return p(v) ? f(v) : g(v).chain(step) }(x));
return eq(a, b);
};
But this must be tested with small inputs as step
can blow the stack :D
If we call first law identity
then what the name should be for this one? /cc @SimonRichardson
Update: or we could remove first law as it's just specific case of this one and call it identity
or something else.
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.
Without putting much thought into it, equivalence looks like it fits the
bill. If anyone disagrees then chime in.
On Sun, 4 Sep 2016, 06:48 Irakli Safareli, [email protected] wrote:
In README.md
#152 (comment)
:@@ -305,6 +306,30 @@ method takes one argument:
chain
must return a value of the same Chain+### ChainRec
+
+A value that implements the ChainRec specification must also implement the Chain specification.
+
+1.T.chainRec((done, next, value) => g(done(value)), i)
is equivalent tog(i)
@rpominov https://github.com/rpominov type of g is a => T a. maybe I
have not clearly expressed that with words. it looks like there is no word
on f/g, for example in monad spec . should I remove the line about g? or
what you think is best wording for that?What you think on something like this?
g returns value of type T which contains value of it's first argument
type.
About the other laws we could state this
T.chainRec((next,done,v) => p(v) ? f(v).map(done) : g(v).map(next), i) is
equivalent to(function step(v) {
return p(v) ? f(v) : g(v).chain(step)
}(i))But chainRec must use constant stack space
And types would be:
p :: a -> boolg :: a -> T bf :: a -> T astep :: a -> T b
And we could also include this in laws like this:
const someNameHere = t => eq => p => f => g => x => {
const a = t.chainRec((next,done,v) => p(v) ? f(v).map(done) : g(v).map(next), x);
const b = (function step(v) { return p(v) ? f(v) : g(v).chain(step) }(x));
return eq(a, b);
};But this must be tested with small inputs as step can blow the stack :D
If we call first law identity then what the name should be for this one?
/cc @SimonRichardson https://github.com/SimonRichardson—
You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub
https://github.com/fantasyland/fantasy-land/pull/152/files/992f729a420f0ce1343ae67c052b050374329d55#r77447076,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACcaGD2dTr27kK97thrpMa2deKQxuDuUks5qmluqgaJpZM4J0S7m
.
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.
type of
g
isa => T a
@safareli You've convinced me again that the law is correct. I don't know why my mind keep going back and forth on this :)
But if we have "equivalence" law, the first one seems redundant.
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.
equivalence
seems good. will remove first one
I'm curious maybe we could make it instance method: - T.chainRec(f, i)
+ T.of(i).chainRec(f) ? |
I think type should look like this:
Edit: #152 (comment) made me realize that |
We should depend least powerful specification. Now we only require structure to implement T.prototype.monadRec = function(f){
return this.chain(v => T.chainRec(f, v))
} // type of `lastPagePostLikes` is `Task Number`
const lastPagePostLikes = Task.chainRec(
(
// this function creates some special value from one argument
// the argument is of same type as `i`, `Page` in this case, it does the recursion
next,
// this function creates some special value from one argument
// value wrapped in `done` will be in `Task`as result of `Task.chainRec`
// the argument type is `Number` in this case
done,
// on first call it's the `i` and every other invocation
// should be of same type `Page` but in JS you can not do that :p
page
) => (
page.nextPageURI
// if current page has url to next page load the next page
// here we have type `Task Page`
? HTTP.fetchJSON(page.nextPageURI)
// here type would be `Task c`
// but the `c` would be some `Either` like structure
// so it would be like `Either Page Number`
.map(next)
//if we don't have next page url then this is the last page and return post likes
// type here is `Task Number`
: Task.of(page.posts.map(getLikes))
// as this is what we wanted to actually get as the result wrap it in `done`
// here type would be `Task c`
// but the `c` would be some `Either` like structure
// so it would be like `Either Page Number`
.map(done)
),
// this is `i` initial value. it's type is `Page`
{ nextPageURI : 'posts/1' , posts:[...]}
) So type of (
(Page -> Either Page Number) ->
(Number -> Either Page Number) ->
Page ->
Task (Either Page Number)
) -> Page -> Task Number Replace (
(a -> Either a b) ->
(b -> Either a b) ->
a ->
Task (Either a b)
) -> a -> Task b As value returned from (
(a -> c) ->
(b -> c) ->
a ->
Task c
) -> a -> Task b Replace ((a -> c) -> (b -> c) -> a -> m c ) -> a -> m b |
@SimonRichardson should I also add the |
Same name to keep consistency, that would be great 👍 On Sat, 3 Sep 2016, 20:48 Irakli Safareli, [email protected] wrote:
|
I'm asking name of the law, like for monad we have One thing that comes to my mind is to call it |
Is that not just identity? On Sat, 3 Sep 2016, 21:03 Irakli Safareli, [email protected] wrote:
|
I still think it's good idea to make it depend on Monad, and use instance method as proposed in #152 (comment) It's true that we should depend on least powerful specification. But in FL we should also try to use instance methods if possible. That's how FL works — it allows to write generic code based on instance methods. And I don't think we loose much if we make it dependent on Monad. Can we imagine a type that cannot implement |
- remove spaces at the end of some lines - add newline at the end of id.js
- update ChainRec law - add chainRec to namespace - add laws/chainrec.js - add ChainRec implementation to Id - add test for ChainRec instance of Id
I could move clean up commit in different PR if necessary |
Maybe we don't need dependency on Monad for
Yea, we should at least do this for consistency with |
👍 |
|
||
A value that implements the ChainRec specification must also implement the Chain specification. | ||
|
||
1. ```js |
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.
[minor] These should use single backticks (inline code block) for consistency.
This will now need a type signature has the PR #147 has landed. |
* 'master' of github.com:fantasyland/fantasy-land: Add ChainRec specification (fantasyland#152) Add type signatures to spec (fantasyland#147)
Type of
ChainRec
is:Fixes #151