I am trying to learn how to use the built-in laziness in Scala by implementing my own version of lazy lists:
object LazyList { def empty[A] : LazyList[A] = new LazyList[A] { lazy val uncons = None } def cons[A](h : => A, t : => LazyList[A]) : LazyList[A] = new LazyList[A] { lazy val uncons = Some( (h,t) ) } def from(s : Int) : LazyList[Int] = new LazyList[Int] { lazy val uncons = Some( (s,from(s + 1)) ) } } trait LazyList[A] { import LazyList._ def uncons : Option[(A,LazyList[A])] def fmap[B](f : A => B) : LazyList[B] = uncons match { case None => empty case Some( (h,t) ) => cons(f(h),t.fmap(f)) } def take(i : Int) : LazyList[A] = uncons match { case None => empty case Some( (h,t) ) => if (i <= 0) empty else cons(h,t.take(i - 1)) } override def toString : String = uncons match { case None => "[]" case Some( (h,t) ) => "[" ++ h.toString ++ ",..]" } }
This works, and I can, for example, map { _ + 2} to an infinite list:
> LazyList from 1 fmap { _ + 2 } res1: LazyList[Int] = [2,..]
I decided to implement some functions that I usually use as drop , take , etc., and I was able to implement them, except inits . My inits implementation:
def inits : LazyList[LazyList[A]] = uncons match { case None => empty case Some( (h,t) ) => cons(empty,t.inits.fmap(cons(h,_))) }
The problem is that for some reason it does not work on endless lists. I cannot, for example, write:
> LazyList from 1 inits
because it works forever. It seems that after t.inits problem is fmap , which for some reason breaks laziness (if I delete fmap , this is wrong, but lazy). Why fmap provide strictness and, given my LazyList type, how can I implement inits so that it works in infinite lists?