Skip to content
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

Lazy foldM for "Iterables" #1414

Merged
merged 4 commits into from
Oct 22, 2016
Merged

Conversation

peterneyens
Copy link
Collaborator

If I am understanding correctly, now that FlatMap has tailRecM we can revisit #1030.

I adapted an old commit by @ceedubs for a PR to add MonadRec by @TomasMikula.

ceedubs and others added 2 commits October 19, 2016 11:15
Resolves typelevel#1030, though it may be desirable change the default
implementation to be lazy in the future. This can be done with `FreeT`
but as of now I'm not sure how to do it without.
@codecov-io
Copy link

codecov-io commented Oct 19, 2016

Current coverage is 91.67% (diff: 100%)

Merging #1414 into master will decrease coverage by <.01%

@@             master      #1414   diff @@
==========================================
  Files           240        240          
  Lines          3608       3617     +9   
  Methods        3540       3555    +15   
  Messages          0          0          
  Branches         67         61     -6   
==========================================
+ Hits           3308       3316     +8   
- Misses          300        301     +1   
  Partials          0          0          

Powered by Codecov. Last update 7ea2024...6f3da09

@TomasMikula
Copy link
Contributor

Some structures, such as Vector or a balanced tree, allocate new memory in tail, but usually not in Iterator.next. Thus I believe that an implementation that uses an iterator instead of repeatedly calling tail can be significantly more efficient in some cases.

@@ -372,4 +378,12 @@ object Foldable {
Eval.defer(if (it.hasNext) f(it.next, loop()) else lb)
loop()
}

def iteratorFoldM[M[_], A, B](it: Iterator[A], z: B)(f: (B, A) => M[B])(implicit M: Monad[M]): M[B] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we also mention here in the doc the incentive of this special implementation for Iteratables?

@non
Copy link
Contributor

non commented Oct 21, 2016

The code seems good to me. 👍 Thanks!

def iteratorFoldM[M[_], A, B](it: Iterator[A], z: B)(f: (B, A) => M[B])(implicit M: Monad[M]): M[B] = {
val go: ((B, Iterator[A])) => M[Either[(B, Iterator[A]), B]] = { case (b, it) =>
if (it.hasNext) M.map(f(b, it.next))(b1 => Left((b1, it)))
else M.pure(Right(b))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Iterator is mutable, I would keep it private to the method (otherwise we lose referential transparency). I would keep the signature as you had before, and only use Iterator inside.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this was not clear from my previous comment.

@TomasMikula
Copy link
Contributor

Not sure what I did wrong that in this conversation it shows my newest comment as outdated. I will repeat here:

Since Iterator is mutable, I would keep it private to the method (otherwise we lose referential transparency). I would keep the signature as you had before, and only use Iterator inside.

Sorry if this was not clear from my previous comment.

@non
Copy link
Contributor

non commented Oct 21, 2016

@TomasMikula in the past we've exposed other methods using Iterator to make it easier for third-parties to build their instances (e.g. iterateRight). We could have the method take something like () => Iterator[A] if that makes it seem better.

@TomasMikula
Copy link
Contributor

Well, in that case, ship it!

@TomasMikula
Copy link
Contributor

Another, perhaps premature, optimization would be to avoid allocating the tuple in each step by accessing the iterator from the outer scope instead of passing it along.

@TomasMikula
Copy link
Contributor

LGTM 👍

@kailuowang
Copy link
Contributor

👍 thanks very much!

@kailuowang kailuowang merged commit f90245d into typelevel:master Oct 22, 2016
@peterneyens peterneyens deleted the lazy-foldM branch October 22, 2016 00:45
@johnynek
Copy link
Contributor

Yeah, I'm concerned everything except List will be slower here.
On Wed, Oct 19, 2016 at 05:20 Tomas Mikula notifications@github.com wrote:

Some structures, such as Vector or a balanced tree, allocate new memory
in tail, but usually not in Iterator.next. Thus I believe that an
implementation that uses an iterator instead of repeatedly calling tail
can be significantly more efficient in some cases.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#1414 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAEJdqvkN0GhLFT8WkSM-O-bt1_AyOh3ks5q1jVIgaJpZM4KavX1
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants