I know there are already some ways of making traversal generic in scala, but I'm trying to define a simple trait that encapsulates a traversable type. However, I'm having trouble getting the type signatures to work out. A first attempt would be something like the following:
trait Traversable1[B] {
def head: Option[B]
def next: Traversable1[B]
}
This works well, except I want next to return an object that has the same type as the original traversable, not the more generic traversable type. For example, if List has the trait Traversable1, it's next method should return something of type List, not Traversable1. Thus, my second attempt was the following:
trait Traversable2[A[B] <: Traversable2[A[B]] {
def head: Option[B]
def next: A[B]
}
Here, A could equal List, so next would return a List[B]. Unfortunately, this code does not compile: A[B] takes no type parameters, expected: one
How can I achieve what I'm trying to do?
You could create a trait like:
trait Traversable[T[_]] {
def head[A](t: T[A]): Option[A]
def next[A](t: T[A]): T[A]
}
then implement it for lists like:
implicit val listTraversable = new Traversable[List] {
def head[A](l: List[A]) = l match {
case Nil => None
case x::_ => Some(x)
}
def next[A](l: List[A]) = l.tail
}
You can then make use of it using the 'type class' pattern by taking an implicit implementation of the trait to do the work e.g.
def foreach[T[_], A](t: T[A], f: A => Unit)(implicit trav: Traversable[T]): Unit = {
trav.head(t) match {
case Some(v) =>
f(v)
foreach(trav.next(t), f)
case None => ()
}
}
and call it with
foreach(List(1,2,3), println)
Related
I am trying to get familiar with higher-kinded types in Scala and so I tried implementing this simple method that takes a traversable of Option and flattens it just like flattening it normally would. However, the compiler is raising an error because the function returns type Traversable[Any] instead of T[S]. Why is this, and how can I make it work correctly?
def flatten[S, T[_] <: Traversable[_]](list: T[Option[S]]): T[S] = {
list.collect({ case Some(s) => s })
}
I think that maybe I'm defining the type of T incorrectly, but I also tried T[_]: Traversable and T[X] <: Traversable[X] and those didn't work either.
Of course, this works:
def flatten[S](list: Traversable[Option[S]]): Traversable[S] = {
list.collect({ case Some(s) => s })
}
But I don't want to lose the input type information on the return type (calling flatten(List[Option[T]]) should return List[T].
This is because collect doesn't return a T, it returns only a Traversable. The trait Traversable doesn't know the type of whatever class is inheriting it.
Furthermore, your higher-kinded type is wrong, it should be T[x] <: Traversable[x] to avoid weirdness with existentials. You could do something like this:
def flatten[S, T[x] <: Traversable[x]](list: T[Option[S]])(
implicit ev: collection.generic.CanBuildFrom[Traversable[Option[S]], S, T[S]]
): T[S] = list.collect { case Some(s) => s }
, or you might be better off with Luis Miguel Mejía Suárez's suggestion of using typeclasses. I would also suggest using Scala 2.13 if possible.
trait Flatten[F[_]] {
def flatten[S](list: F[Option[S]]): F[S]
}
object Flatten {
def flatten[S, F[_]](list: List[Option[S]])(implicit f: Flatten[F]) = f.flatten(list)
implicit val flattenList = new Flatten[List] {
def flatten[S](list: List[Option[S]]) = list.collect { case Some(s) => s }
}
}
I want to do something really simple, but I'm struggling to craft the correct search or just understanding some of the solutions I've seen.
Given a method that takes a generic type parameter which is a Coproduct;
def apply[T <: Coproduct] = {
...
}
How can I iterate over the types that form the coproduct? Specifically, for each type that is a case class, I'd like to recursively examine each field and build up a map with all the information.
Currently I'm working around this using a builder pattern, which I'll post here in case it's useful to others;
class ThingMaker[Entities <: Coproduct] private {
def doThings(item: Entities): Set[Fact] = {
...
}
def register[A <: Product with Serializable]: ThingMaker[A :+: Entities] = {
// useful work can be done here on a per type basis
new ThingMaker[A :+: Entities]
}
}
object ThingMaker {
def register[A <: Product with Serializable]: ThingMaker[A :+: CNil] = {
// useful work can be done here on a per type basis
new ThingMaker[A :+: CNil]
}
}
If you just want to inspect values, you can simply pattern match on a coproduct like on any other value...
def apply[T <: Coproduct](co: T): Any = co match {
case Inl(MyCaseClass(a, b, c)) => ???
...
}
...but if you want to be more precise than that, for instance to have a return type that depends on the input, or to inspect the types inside this coproduct to summon implicits, then you can write the exact same pattern matching expression using a type class and several implicit definition:
trait MyFunction[T <: Coproduct] {
type Out
def apply(co: T): Out
}
object MyFunction {
// case Inl(MyCaseClass(a, b, c)) =>
implicit val case1 = new MyFunction[Inl[MyCaseClass]] {
type Out = Nothing
def apply(co: Inl[MyCaseClass]): Out = ???
}
// ...
}
In general, when you want iterate over all types of a coproduct, you will always follow the same tail recursive structure. As a function:
def iterate[T <: Coproduct](co: T): Any = co match {
case Inr(head: Any) => println(v)
case Inl(tail: Coproduct) => iterate(tail)
case CNil => ???
}
Or as a "dependently typed function":
trait Iterate[T <: Coproduct]
object Iterate {
implicit def caseCNil = new Iterate[CNil] {...}
implicit def caseCCons[H, T <: Coproduct](implicit rec: Iterate[T]) =
new Iterate[H :+: T] {...}
}
You can for instance ontain the name of each type in a coproduct using an addition ClassTag implicit:
trait Iterate[T <: Coproduct] { def types: List[String] }
object Iterate {
implicit def caseCNil = new Iterate[CNil] {
def types: List[String] = Nil
}
implicit def caseCCons[H, T <: Coproduct]
(implicit
rec: Iterate[T],
ct: reflect.ClassTag[H]
) =
new Iterate[H :+: T] {
def types: List[String] = ct.runtimeClass.getName :: rec.types
}
}
implicitly[Iterate[Int :+: String :+: CNil]].types // List(int, java.lang.String)
Because of the way Scala lets you influence implicit priority, it's actually possible to translate any recursive function with pattern matching into this "dependently typed function" pattern. This is unlike Haskell where such function can only be written if call cases of the match expression are provably non-overlapping.
Below code causes compiler error :
Multiple markers at this line
- not found: type A
- not found: type A
at line def headOption :
object LazyList {
println("Welcome to the Scala worksheet")
sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
object Stream {
def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
lazy val head = hd
lazy val tail = tl
Cons(() => head, () => tail)
}
def empty[A]: Stream[A] = Empty
def apply[A](as: A*): Stream[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())
}
}
}
But I think the function is defined correctly ? Since A is a generic type it should not cause this compiler error ?
You need to move def headOption into the Stream trait. Currently, it's in the Stream companion object. An object doesn't have a type parameter, because an object is just one determinate thing in memory, with a completely specific type. The Stream trait describes many possible Stream objects, each of which can have a different type, corresponding to the type filled in for A when the object is created.
Notice that you intend your this in headOption to refer to a specific Stream, not to the Stream companion object.
headOption should be defined as polymorphic function like what you did for empty and apply. So you need to add type annotation to your function as follow:
def headOption[A]: Option[A] = ...
However generally headOption should be moved to the trait and defining it in companion object is wrong. If you move it to the trait, the trait is typed so you do not need to add type annotation to the function and your current implementation will work.
I am trying to write a Value Class to add functionality to anything that implements Seq[_] and allow it to make batch calls that return a Future[_] (specifically, I am using it to make batch REST calls).
final class BatchedList[A, C[X] <: Seq[X]](val targetList: C[A]) extends AnyVal {
def batchRequests[B](batchSize: Int)(runner: Seq[A] => Seq[Future[Either[Result, B]]])
(implicit bf: CanBuildFrom[C[A], Either[Result, B], C[Either[Result, B]]]): Future[Either[Result, C[B]]] = {
targetList.grouped(batchSize).foldLeft(Future.successful(bf(targetList))) { (results, set) =>
results flatMap { responses =>
Future.sequence(runner(set)).map(responses ++=)
}
} map {
_.result().sequenceU
}
}
}
However, I can't get this to compile. I keep receiving the compiler error
value sequenceU is not a member of type parameter C[Either[play.api.mvc.Result,B]]
I've imported both scalaz._ and Scalaz._, and I know they've provided a Traverse[_] for my use case (which is List[_] in this example). I'm pretty sure this is some sort of implicit resolution issue with the types, but I'm stumped on how to proceed forward resolving it.
I believe that this happens because Scalaz doesn't provide typeclass instances for Seq but instead for IndexedSeq and List. As such, you'll need to provide the Traverse instance yourself (note the additional implicit argument C to batchRequests):
final class BatchedList[A, C[X] <: Seq[X]](val targetList: C[A]) extends AnyVal {
def batchRequests[B](batchSize: Int)(runner: Seq[A] => Seq[Future[Either[Result, B]]])
(implicit bf: CanBuildFrom[C[A], Either[Result, B], C[Either[Result, B]]], C: Traverse[C]): Future[Either[Result, C[B]]] =
targetList.grouped(batchSize).foldLeft(Future.successful(bf(targetList))) { (results, set) =>
results flatMap { responses =>
Future.sequence(runner(set)).map(responses ++=)
}
} map {
_.result().sequenceU
}
}
As you can see, this will return a sequence type corresponding to the type provided as C:
scala> def run[A](s: Seq[A]): Seq[Future[Either[Result, A]]] =
| s.map(i => Future.successful(Right(i)))
run: [A](s: Seq[A])Seq[scala.concurrent.Future[Either[Result,A]]]
scala> :t new BatchedList(List(1,2,3)).batchRequests(1)(run)
scala.concurrent.Future[Either[Result,List[Int]]]
scala> :t new BatchedList(Vector(1,2,3)).batchRequests(1)(run)
scala.concurrent.Future[Either[Result,scala.collection.immutable.Vector[Int]]]
If you always want it to return a Seq it's a simple matter of an upcast.
How it's done in Scala:
sealed trait Option[+A] {
def get: A
def isEmpty: Boolean
def map[B](f: A => B): Option[B] =
if (isEmpty) None else Some(f(this.get))
}
object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
How I would assume it in OOP world:
sealed trait Option[+A] {
def map[B](f: A => B): Option[B]
}
object None extends Option[Nothing] {
def map[B](f: Nothing => B): Option[B] = this
}
case class Some[+A](get: A) extends Option[A] {
def map[B](f: A => B): Option[B] = Some(f(get))
}
What's wrong with the latter?
Functional programming in Scala is using match in Option[A] trait, which is the third way (looks like Haskell, but why?) Why not utilize subtype polymorphism?
UPDATE: Third way I mentioned:
sealed trait Option[+A] {
def map[B](f: A => B): Option[B] = this match {
case None => None
case Some(a) => Some(f(a))
}
}
object None extends Option[Nothing] {
}
case class Some[+A](get: A) extends Option[A] {
}
I'm not sure whether you intended to, but you left out the declarations of isEmpty and get, which are needed by anyone that wants to check the contents of an arbitrary Option without needing to downcast to Some. Since both of these methods need to be defined by both subclasses, and since map can be defined in terms of them, I think the reasoning was that it would be better to define map in one place leveraging the subclass implementations of the other methods, rather than defining map in three places.
I guess that since scala support both functional and imperative and it aiming java programs, this is one of the stuff that make them fill more comfortable.
From java programmer
val opt: Option[String] = ???
if (!opt.isEmpty) {
//do something with opt.get
} else {
//do some default...
}
may be more understandable than functional way (even with getOrElse).
#Victor Moroz wrote in one of the comments about using pattern matching instead of the this match { None => default_val; Some(v) => v }
This in addition to be harder to read by programmer new to the functional world, will cost much more, since Option is intended to be use a lot, and instanceOf cost more than a simple if.