0
votes

I'm working on writing the Stream class in Chapter 5 of Functional Programming in Scala, I know the solutions are online, but it's not helping me. I faced the same issue with the previous Chapter writing the List class.

I got so frustrated I actually COPY PASTED from the solution to my Scala worksheet and still the same issue.

I thought maybe it's because of the name (there's already a List and Stream), doesn't seem like a smart idea to name them like this, so I changed it, didn't help.

Maybe it's something to do with Intellij (I'm using IntelliJ IDEA), I'm doing the exercises on the Scala Worksheets. But I can't find anything about this issue in relation to IDEs.

Here is what I have so far:

sealed trait StreamRED[+A]
case object Empty extends StreamRED[Nothing]
case class Cons[+A](h: () => A, t: () => StreamRED[A]) extends StreamRED[A]

object StreamRED {
  def cons[A](hd: => A, tl: => StreamRED[A]): StreamRED[A] = {
    lazy val head = hd
    lazy val tail = tl
    Cons(() => head, () => tail)
  }
  def empty[A]: StreamRED[A] = Empty

  def apply[A](as: A*): StreamRED[A] =
    if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))

  def headOption: Option[A] = this match {
    case Empty => None
    case Cons(h,t) => Some(h())
  }

  def toList: List[A] = {
    @annotation.tailrec
    def go(s: StreamRED[A], acc: List[A]): List[A] = s match {
      case Cons(h,t) => go(t(), h() :: acc)
      case _ => acc
    }
    go(this, List()).reverse
  }
}

I get the following errors:

"Cannot resolve symbol A" on the A in Option[A] (in headOption method) and List[A] and StreamRED[A] (in toList)

"Type mismatch. Required: StreamRED[Any], Found: StreamRED.type" on the this in toList.

"Pattern type is incompatible with expected type, found: Empty.type, required: StreamRED.type" on the Empty in headOption.

New to Scala, new to IntelliJ, new to statically typed languages, new to FP. Any explanations and recommendations for good reading materials much appreciated.

1

1 Answers

1
votes

The two functions toList and headOption cannot be defined in the companion object of StreamRED.

If you define them directly in the trait it works:


sealed trait StreamRED[+A] {

  def headOption: Option[A] = this match {
    case Empty => None
    case Cons(h,t) => Some(h())
  }

 def toList: List[A] = {
    @annotation.tailrec
    def go(s: StreamRED[A], acc: List[A]): List[A] = s match {
      case Cons(h,t) => go(t(), h() :: acc)
      case _ => acc
    }
    go(this, List()).reverse
  } 
}

case object Empty extends StreamRED[Nothing]
case class Cons[+A](h: () => A, t: () => StreamRED[A]) extends StreamRED[A]

object StreamRED {
  def cons[A](hd: => A, tl: => StreamRED[A]): StreamRED[A] = {
    lazy val head = hd
    lazy val tail = tl
    Cons(() => head, () => tail)
  }
  def empty[A]: StreamRED[A] = Empty

  def apply[A](as: A*): StreamRED[A] =
    if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))
}

A word of warning: Pattern matching on this is feels to me like bad practice. You know exactly what this is. Implement the functions in Empty and Cons instead.

Do this instead:

sealed trait StreamRED[+A] {
  def headOption: Option[A]
  def toList: List[A]
}

case object Empty extends StreamRED[Nothing] {
  def headOption: Option[Nothing] = None
  def toList: List[Nothing] = List()
}


case class Cons[+A](h: () => A, t: () => StreamRED[A]) extends StreamRED[A] {
  def headOption: Option[A] = Some(h())
  def toList: List[A] = h() +: t().toList
}

object StreamRED {
  def cons[A](hd: => A, tl: => StreamRED[A]): StreamRED[A] = {
    lazy val head = hd
    lazy val tail = tl
    Cons(() => head, () => tail)
  }
  def empty[A]: StreamRED[A] = Empty

  def apply[A](as: A*): StreamRED[A] =
    if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))
}