Creating own List replica in Scala - scala

I am reading Functional Programming in Scala from Manning, authored by Paul Chiusano and Runar Bjarnason. In its 3rd chapter, there is a code to create a List and there are assignments to implement various methods of the list. Following is partial implementation of the my List
package src.Cons
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
object List {
//my issue is I do not want to pass a list to sum but want to use objectName.sum notation
def sum(ints:List[Int]):Int = ints match {
case Nil => 0
case Cons(x,xs) => x+sum(xs)
}
}
Question - How can I create my list such that I can call l.sum instead of List.sum(l)?

You can "PmL", as #Gabriele Petronella has suggested, or you can move the sum() method to the Cons class, as #DeadNight wrote, but before either of those can work you have to resolve the current conflict between your List object and your List trait.
The sum() in your List object can only sum a List[Int] but your class definitions use a more generic type member and, as such, you can't use + because the compiler doesn't know how to add two A types.
If you want to restrict your List to only handling numeric types then this will work.
case class Cons[A: Numeric](h:A, t:List[A]) extends List[A] {
def sum: A = List.sum(this)
}
object List {
def sum[A](ints:List[A])(implicit ev: Numeric[A]):A = ints match {
case Nil => ev.zero
case Cons(x,xs) => ev.plus(x, sum(xs))
}
}
val x = Cons(4, Cons(2, Nil))
x.sum // res0: Int = 6

Making sum a member
The problem is, you don't know how to sum the List[A] for every type A, only a List[Int]. If there was a way to allow calls when A is an Int...
Let's take a look at the standard library for that. We're interested in Option#flatten method because:
val o1 = Option(Option(3)).flatten // compiles
val o2 = Option(4).flatten // does not compile
Notice the weird implicit ev: <:<[A, Option[B]]. This is the key here - it's a thing that compiler provides for you, but only if it is known at compile time, that your Option[A] is a subtype of Option[Option[B]] for some type B. This is the trick that we can use.
sealed trait List[+A] {
def sum(implicit ev: A <:< Int): Int = this match {
case Nil => 0
case Cons(x, xs) => x + xs.sum // <- here x is magically converted to Int, so we can use plus
}
}
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
println(Cons(4, Cons(38, Nil)).sum) // 42
ScalaFiddle
Notice that you can write <:<[A, B] as A <:< B.
NB: there's also =:=[A, B] type, for when your A is exactly Int - you can use either of those
Doing better?
Actually, std library has sum method and it's type is even weirder:
def sum(implicit ev: Numeric[A]). Doing so allows it to work on any number-like type like Double and Int, and has the operations for comparison, subtraction, multiplication, etc. So you can make it even more generic. I suggest you do it after reading a chapter about Monoids, tho :)

You can use the so-called "Pimp my Library" pattern.
Define an implicit class ListOps
implicit class ListOps[+A](list: List[A]) {
def sum = List.sum(this)
}
and now you can call list.sum. The implicit conversion will be triggered and the compiler will interpret it as ListOps(list).sum.

Move the definition of sum inside the definition of List trait
You can leave the concrete definitions to Nil & Cons
package src.Cons
sealed trait List[+A] {
def sum: Int
}
case object Nil extends List[Nothing] {
val sum: Int = 0
}
case class Cons[+A](h:A, t:List[A]) extends List[A] {
def sum: Int = h + t.sum
}

Related

How to implement subtype resolution of typeclass in scala

I want to understand how to go about implementing the following use-case using typeclasses in Scala (or find out if it is even possible).
Given a sealed trait and a couple of concrete cases:
sealed trait Base
case class Impl1() extends Base
case class Impl2() extends Base
Given a typeclass operating on Base and an instance for each of the corresponding base implementations:
trait Processor[B <: Base]:
def process(b: B): String
given Processor[Impl1] with:
def process(b: Impl1): String = ??? // not important
given Processor[Impl2] with:
def process(b: Impl2): String = ??? // not important
Given a list of base objects:
val objects: List[Base] = ??? // whatever
Is it possible to implement a method that goes something like this?
val processed = objects.map(obj => process(obj))
def process[B <: Base](b: B)(using proc: Processor[B]) = proc.process(b)
When I try to naively implement the above as-such, the compiler complains that it can't find an implicit for Processor[Base], which I guess it makes sense, since in the context of the method call for process(obj), the obj val has the Base type.
What I would like to do is to let the compiler figure out the concrete type of obj, fetch the corresponding given instance for the concrete type and inject it into the process method. Is it even possible to do such a thing? Does it even make sense?
(Note - I've written my code in scala 3, but I'll gladly accept an answer in scala 2 syntax).
With a list of base objects you won't be able to do it, the list doesn't contain the real type at compile time and the compiler won't be able to find their respective type classes.
In scala 3 the typed list is the tuple, so you can use a tuple to have a typed list and obtain what you are expecting. The method you need is and adaptation of the one you can find with the same name in the scala 3 documentation and is simple as:
inline def summonAll[T <: Tuple](tup: T): List[String] =
inline tup match
case _: EmptyTuple => Nil
case tupl: (t *: ts) => summonInline[Processor[t]].process(tupl.head) :: summonAll[ts](tupl.tail)
and you can use it only passing the list of elements you want to obtain in a tuple in the expected type:
summonAll[(A, B)]
Here you can see the following full code running:
import scala.compiletime.{erasedValue, summonInline}
trait Processor[A]:
def process(t: A): String
object Processor:
def apply[T: Processor]: Processor[T] = summon[Processor[T]]
given Processor[String] with
def process(t: String): String = "im String: " + t
given Processor[Int] with
def process(t: Int): String = "im Int: " + t
inline def summonAll[T <: Tuple](tup: T): List[String] =
inline tup match
case _: EmptyTuple => Nil
case tupl: (t *: ts) => summonInline[Processor[t]].process(tupl.head) :: summonAll[ts](tupl.tail)
val t1 = ("hi", "bye", 44 )
val t2 = summonAll(t1)
println(t2) //List(im String: hi, im String: bye, im Int: 44)
With this particular typeclass you can easily create the Processor[Base] which the compiler asks for:
given Processor[Base] with:
def process(b: Base): String = b match
case b: Impl1 => process(b)
case b: Impl2 => process(b)
Using type class derivation you can also generate it automatically, but I am afraid it's going to be far more code! And if you enable warnings (which you should), the compiler should warn you about a non-exhaustive match if a new subclass is added anyway. So I'd use this manual version unless you have quite a lot of subclasses to handle in your actual case or they change often.
Note "this particular typeclass" it wouldn't work e.g. if process took more than 1 B parameter, or some parameters with more complex types containing B.

Restricting a method to a subset of values of the caller's type parameter

Summary: I want to add an instance method to instances of a parametrized type, but only for some values of the type parameter. Specifically, I have List[E], but I only want instances of List[List[_]] to have a flatten() method.
I am learning the basics of Scala and functional programming by following along with the exercises in Functional Programming in Scala by Chiusano & Bjarnason.
Suppose I have a type List[E] and a companion object List that has methods for working with instances of List[E].
sealed trait List[+E]
case object Nil extends List[Nothing]
case class Cons[+E](head: E, tail: List[E]) extends List[E]
object List {
def flatten[E](aListOfLists: List[List[E]]): List[E] = Nil
def foldLeft[E, F](aList: List[E])(acc: F)(f: (F, E) ⇒ F): F = acc
}
Now suppose I want to create analogous methods on List instances that simply forward the calls to the companion object. I would try to augment the trait definition as follows.
sealed trait List[+E] {
def foldLeft[F](acc: F)(f: (F, E) => F) = List.foldLeft(this)(acc)(f)
}
I run into a complication: List.foldLeft() works with any List[E], but List.flatten() expects a List[List[E]] argument. Thus, I only want instances of List[List[_]] to have this method. How can I add flatten() to the appropriate subset of List instances? How do I use Scala's type system to express this restriction?
We can build up what we need piece by piece. First we know that we need a type parameter for our flatten, since we don't otherwise have a way to refer to the inner element type:
sealed trait List[+E] {
def flatten[I] // ???
}
Next we need some way of establishing that our E is List[I]. We can't add constraints to E itself, since in many cases it won't be List[I] for any I, but we can require implicit evidence that this relationship must hold if we want to be able to call flatten:
sealed trait List[+E] {
def flatten[I](implicit ev: E <:< List[I]) = ???
}
Note that for reasons related to variance (and type inference) we need to use <:< instead of =:=.
Next we can add the return type, which we know must be List[I]:
sealed trait List[+E] {
def flatten[I](implicit ev: E <:< List[I]): List[I] = ???
}
Now we want to be able to call List.flatten on a List[List[I]]. Our ev allows us to convert values of type E into List[I], but we don't have E values, we just have a List[E]. There are a number of ways you could fix this, but I'll just go ahead and define a map method and use that:
sealed trait List[+E] {
def map[B](f: E => B): List[B] = this match {
case Nil => Nil
case Cons(h, t) => Cons(f(h), t.map(f))
}
def flatten[I](implicit ev: E <:< List[I]): List[I] = List.flatten(map(ev))
}
And then:
val l1 = Cons(1, Cons(2, Nil))
val l2 = Cons(3, Cons(4, Cons(5, Nil)))
val nested = Cons(l1, Cons(l2, Nil))
val flattened: List[Int] = nested.flatten
This won't actually work, since your List.flatten is broken, but it should when you fix it.

Why can't I call function on companion object?

In the code below, why can't I call the sum function when I create an instance of fpinscala.datastructures.List? I.e. in the SBT console I do the following:
scala> :paste -raw exercises/src/main/scala/fpinscala/datastructures/List.scala
scala> val list = fpinscala.datastructures.List(2,3)
scala> list.sum(fpinscala.datastructures.List(2,3))
I guess my problem is I don't really understand the companion object - although my understanding was that it just defined functions on the type I have created which I could then call?
package fpinscala.datastructures
sealed trait List[+A] // `List` data type, parameterized on a type, `A`
case object Nil extends List[Nothing] // A `List` data constructor representing the empty list
/* Another data constructor, representing nonempty lists. Note that `tail` is another `List[A]`,
which may be `Nil` or another `Cons`.
*/
case class Cons[+A](head: A, tail: List[A]) extends List[A]
object List { // `List` companion object. Contains functions for creating and working with lists.
def sum(ints: List[Int]): Int = ints match { // A function that uses pattern matching to add up a list of integers
case Nil => 0 // The sum of the empty list is 0.
case Cons(x,xs) => x + sum(xs) // The sum of a list starting with `x` is `x` plus the sum of the rest of the list.
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x,xs) => x * product(xs)
}
def apply[A](as: A*): List[A] = // Variadic function syntax
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
EDIT: Maybe a better question is, how would I implement the following:
scala> val list = fpinscala.datastructures.List(2,3)
scala> list.sum (should return 5)
First, note that this line:
val list = fpinscala.datastructures.List(2,3)
is the same as the following:
val list = fpinscala.datastructures.List.apply(2,3)
In other words, you are calling the method apply in the List object. The return type of the apply method is the trait List, so the type of the val list is the trait List.
This means that when you call list.sum, you're trying to call the sum method of the trait List. But your trait List does not have a sum method, so it fails.
You have put the sum method in the companion object of trait List - you should put it in the trait instead (removing the parameter; or call the sum in the List object, passing list as the argument, as Seth Tisue noted in his comment).
A trait or class does not automatically have all the methods of its companion object.
Answering question about how to implement list.sum: you should define method in your List trait. Take a look at scala.collection.TraversableOnce 'sum' implementation:
def sum[B >: A](implicit num: Numeric[B]): B = foldLeft(num.zero)(num.plus)
So far List has arbitrary type, implicit instance of Numeric monoid is required - it gives zero value and operation (plus in the case of sum)

Shapeless: Checking Type Constraints of Polymorphic functions

I'm working on a small library for economic models that check the Units of the entities, using Types, e.g. instead of val apples = 2.0 we write val apples = GoodsAmount[KG, Apples](2.0). For creating bundle of goods, I trying to use HLists from the shapeless library. This works fine, but in some cases I can not be as generic code as I prefer. See e.g. the following problem.
I start with a simple code that explain what I want to lift into shapeless. We create two classes, on that represent Km, the other Miles. It should be allowed to add Km classes, but not miles. That I use a abstract type T is mainly motivated be our more complex library. And the indirect call to the '+' function is just because we need something similar in the shapeless case behind.
trait Foo {
type T
val v: Double
def +[B <: Foo](other: B)(implicit ev: this.T =:= other.T) = v + other.v
}
trait _Km
trait _Miles
case class Km(v: Double) extends Foo { type T = _Km }
case class Miles(v: Double) extends Foo { type T = _Miles }
object ExampleSimple extends App {
def add[A <: Foo, B <: Foo](a: A, b: B)(implicit ev: a.T =:= b.T) = { a + b }
add(Km(1), Km(2))
// add(Km(1), Miles(2)) /* does not compile as intended */
}
This works as intended. But it's necessary to have the Type Contraint check on the 'add' function. My attempt to extend this to HLists looks like this:
object ExampleShapeless extends App {
import shapeless._
val l1 = Km(1) :: Km(2) :: HNil
val l2 = Km(4) :: Km(3) :: HNil
object add extends Poly1 {
implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a, b) => a + b }
}
(l1 zip l2).map(add)
}
But this generate the following error message (using Scala 2.10.2):
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:50: Cannot prove that a.T =:= b.T.
[error] implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a: Foo, b) => a + b }
[error] ^
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:54: could not find implicit value for parameter mapper: shapeless.Mapper[ExampleShapeless.add.type,shapeless.::[(Km, Km),shapeless.::[(Km, Km),shapeless.HNil]]]
[error] (l1 zip l2).map(add)
The first error should be fixed, in the case that I could add a Type Constraint to the caseTuple function, but to be honest, I have not understood how the at function is working and where I could add the implicit evidence parameter. And I'm also don't know, what I must do, so that the Mapper would find his implicit value.
A less generic version, where I replase the caseTuple function with
implicit def caseTuple = at[(Km,Km)] { case (a, b) => a + b }
works fine, but would need to write a lot of redundant code (okay, this solution would be still better as our current solution using Tuples). Can somebody give me a hint how I can solve this problem?
Thanks,
Klinke
You can require the type members to match by adding a type parameter to the case:
object add extends Poly1 {
implicit def caseTuple[_T, A <: Foo { type T = _T }] = at[(A, A)] {
case (a, b) => a + b
}
}
Or you could use an existential type, since you only really care that they're the same:
object add extends Poly1 {
implicit def caseTuple[A <: Foo { type T = _T } forSome { type _T }] =
at[(A, A)] {
case (a, b) => a + b
}
}
Either version will provide the behavior you want.

Why is there no Tuple1 Literal for single element tuples in Scala?

Python has (1,) for a single element tuple. In Scala, (1,2) works for Tuple2(1,2) but we must use Tuple1(1) to get a single element tuple. This may seem like a small issue but designing APIs that expect a Product is a pain to deal for users that are passing single elements since they have to write Tuple1(1).
Maybe this is a small issue, but a major selling point of Scala is more typing with less typing. But in this case it seems it's more typing with more typing.
Please tell me:
1) I've missed this and it exists in another form, or
2) It will be added to a future version of the language (and they'll accept patches).
You can define an implicit conversion:
implicit def value2tuple[T](x: T): Tuple1[T] = Tuple1(x)
The implicit conversion will only apply if the argument's static type does not already conform to the method parameter's type. Assuming your method takes a Product argument
def m(v: Product) = // ...
the conversion will apply to a non-product value but will not apply to a Tuple2, for example. Warning: all case classes extend the Product trait, so the conversion will not apply to them either. Instead, the product elements will be the constructor parameters of the case class.
Product is the least upper bound of the TupleX classes, but you can use a type class if you want to apply the implicit Tuple1 conversion to all non-tuples:
// given a Tupleable[T], you can call apply to convert T to a Product
sealed abstract class Tupleable[T] extends (T => Product)
sealed class ValueTupler[T] extends Tupleable[T] {
def apply(x: T) = Tuple1(x)
}
sealed class TupleTupler[T <: Product] extends Tupleable[T] {
def apply(x: T) = x
}
// implicit conversions
trait LowPriorityTuple {
// this provides a Tupleable[T] for any type T, but is the
// lowest priority conversion
implicit def anyIsTupleable[T]: Tupleable[T] = new ValueTupler
}
object Tupleable extends LowPriorityTuple {
implicit def tuple2isTuple[T1, T2]: Tupleable[Tuple2[T1,T2]] = new TupleTupler
implicit def tuple3isTuple[T1, T2, T3]: Tupleable[Tuple3[T1,T2,T3]] = new TupleTupler
// ... etc ...
}
You can use this type class in your API as follows:
def m[T: Tupleable](v: T) = {
val p = implicitly[Tupleable[T]](v)
// ... do something with p
}
If you have your method return the product, you can see how the conversions are being applied:
scala> def m[T: Tupleable](v: T) = implicitly[Tupleable[T]](v)
m: [T](v: T)(implicit evidence$1: Tupleable[T])Product
scala> m("asdf") // as Tuple1
res12: Product = (asdf,)
scala> m(Person("a", "n")) // also as Tuple1, *not* as (String, String)
res13: Product = (Person(a,n),)
scala> m((1,2)) // as Tuple2
res14: Product = (1,2)
You could, of course, add an implicit conversion to your API:
implicit def value2tuple[A](x: A) = Tuple1(x)
I do find it odd that Tuple1.toString includes the trailing comma:
scala> Tuple1(1)
res0: (Int,) = (1,)
Python is not statically typed, so tuples there act more like fixed-size collections. That is not true of Scala, where each element of a tuple has a distinct type. Tuples, in Scala, doesn't have the same uses as in Python.