-
Notifications
You must be signed in to change notification settings - Fork 90
Function.applyN #156
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
Function.applyN #156
Conversation
I was going to mention on your other issue about that - it does seem to me that this is very |
But there are definitely cases where this makes sense, like wanting stack safety or just not wanting to accumulate a large thunk. Just have to think of a simple example to attach here. |
My brain might be a bit rattled now, but I think particularly for the |
|
There's no reason you can't make a stack safe |
A bit confused by this statement, as |
While this is stack safe, and also pretty cool, That said, I feel like this distracts a bit from my main point, which is the one I'm making in the previous comment. Unless I'm misunderstanding how |
This also is substituting materializing an That said, I do still think it's cool, and probably has its place, no? |
Yes on both accounts, but since |
42538d7
to
d514855
Compare
src/Data/Function.purs
Outdated
-- | | ||
-- | If n is less than or equal to 0, the function is not applied. | ||
applyN :: forall a. (a -> a) -> Int -> a -> a | ||
applyN f n = go n |
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.
I think eta-reducing this (applyN f = go
) would be nice, so that there are fewer variables of type Int
in scope, which means there's less to keep in your head while reading this. We then wouldn't need to use a primed identifier in the definition of go
, I think.
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 I think it might not be entirely clear what n
is in the comments: how about this?
The function
applyN f n
applies the functionf
to its argumentn
times.
I think examples using f = (_+1)
are probably best, seeing as there are few other functions and data types in scope here for us to use. Examples using that f
would hopefully be easy to understand, too.
src/Data/Ord.purs
Outdated
@@ -106,6 +105,9 @@ infixl 4 greaterThanOrEq as >= | |||
-- | Compares two values by mapping them to a type with an `Ord` instance. | |||
comparing :: forall a b. Ord b => (a -> b) -> (a -> a -> Ordering) | |||
comparing f = compare `on` f | |||
where |
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.
I think it might be a little nicer to just inline on
here, instead of defining it and using it immediately.
So we need |
d514855
to
1f74794
Compare
Wait no, this is wrong. |
src/Data/Function.purs
Outdated
@@ -77,6 +81,20 @@ applyFlipped x f = f x | |||
-- | ``` | |||
infixl 1 applyFlipped as # | |||
|
|||
-- | Applies the function `f` to its argument `n` times. |
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.
The thing about this is that, while it might be clear what f
and n
are to someone looking at the source, it's not really as clear if you're looking at the docs on Pursuit. You kind of have to be familiar with the convention that n
is for naturals and f
is for functions, which is often safe to assume, but probably best not to in the Prelude. This is why I'd like to say something like "the function applyN f n
applies the function f
..." so that it's clear what f
and n
are. It doesn't have to be that verbatim, but I would like f
and n
to be introduced a little more explicitly.
src/Data/Function.purs
Outdated
applyN :: forall a. (a -> a) -> Int -> a -> a | ||
applyN f = go | ||
where | ||
go n' acc |
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.
Could just use n
here instead of n'
now that it won't shadow?
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.
Will do.
addresses purescript#155
1f74794
to
3cb1fd9
Compare
@hdgarrood I think I've addressed your review comments in this latest amendment. |
I've created a branch of
I'd say that's a difference in efficiency that makes this function worthwhile. This thread has gone in a few different directions, so I'd like to resume, I think in order of importance, why I think this function is worthwhile.
|
Benchmark branch, for the curious: https://github.com/matthewleon/purescript-monoid/tree/bench-endo |
What happens if you use |
Ran them a single time each:
Do keep in mind, though, that All that being said, I don't think the stack issues here are really the main attraction. The constant stack space is a nice "perk," but the speed difference and ergonomics are, I think, more important. |
Yep, agreed. |
Sorry to drag up an old thread, but can I ask why the iteration is not terminated early if you find a fixed point at |
I would say this is because adding an Eq constraint would make it impossible to use with types which aren’t Eq, and also because checking whether we have reached a fixed point each time we recurse could make this much more expensive than it is currently when used with functions which don’t have any fixed points. |
Ah, I had forgotten about the If so, I can open a PR |
I don't think this is function is generally useful enough to earn a spot in Prelude, sorry. |
addresses #155
I haven't thought of a great intuitive example for this, as the places I'm using it are a bit abstruse. A lot of the obvious examples often seem better suited to using
Semigroup
orMonoid
. Any ideas?