How should an invariant List be implemented in Scala? - scala

I want to implement a linked list in Scala where the type parameter is invariant, unlike the standard library List.
Here is an attempt:
sealed trait InvariantList[T]
case object Nil extends InvariantList[_]
case class Cons[T](head : T, tail : InvariantList[T]) extends InvariantList[T]
object InvariantList {
def map[A, B](xs : InvariantList[A])(f : A => B) : InvariantList[B] = {
xs match {
case Nil => Nil[B]
case Cons(head, tail) => Cons(f(head), map(tail)(f))
}
}
}
object Example {
val xs = Cons(7, Cons(5, Nil[Int]))
InvariantList.map(xs)(_ + 1)
}
This is pretty close to what I want, but it is annoying to have to specify the type parameter of Nil in the implementation of map and in the example use.
I also tried using case class Nil[T]() extends InvariantList[T], but this is also ugly because I have to put the parens all over the place.
What is the recommended way of defining Nil in an invariant linked list implementation? I think the question applies more generally to any invariant data structure that has cases with empty constructors. I would like it to be as convenient to use as the standard library List (convenient in that I don't have to specify the type parameter or add parens).

The standard library doesn't have a Nil element for its invariant collections. For example, Set and ListBuffer are invariant, but they do not have Nil sub-type. They require you to say Set.empty[A] or ListBuffer.empty[A].
scala> val l : ListBuffer[Int] = Nil
<console>:11: error: type mismatch;
found : scala.collection.immutable.Nil.type
required: scala.collection.mutable.ListBuffer[Int]
val l : ListBuffer[Int] = Nil
^
scala> val set: Set[Int] = Nil
<console>:11: error: type mismatch;
found : scala.collection.immutable.Nil.type
required: Set[Int]
val set: Set[Int] = Nil
^
Having a single case object Nil extends InvariantList[_] won't work because of invariance. Every list type would require it's own empty representation. Because an empty List[Dog] would not be the same as an empty List[Animal], if Dog <: Animal, etc.
What you're essentially asking for is a singleton symbol Nil to be treated like many different types. I think you're on the right track with a case class, because that will allow you one Nil[A] per list type. Unfortunately that will never allow you to pattern-match on Nil, only Nil(), because you need an extractor with parameters and not one for a singleton (which your Nil is not). If you want the convenience of not writing the type parameter and parentheses, you can write a method like this in the same package:
def nil[A] = Nil[A]()
It's not ideal, but it will partially solve your problem. Putting it all together:
sealed trait InvariantList[A]
case class Nil[A]() extends InvariantList[A]
case class Cons[A](head : A, tail : InvariantList[A]) extends InvariantList[A]
object InvariantList {
def map[A, B](xs : InvariantList[A])(f : A => B) : InvariantList[B] = {
xs match {
case Nil() => nil
case Cons(head, tail) => Cons(f(head), map(tail)(f))
}
}
}
scala> val xs = Cons(7, Cons(5, nil))
xs: Cons[Int] = Cons(7,Cons(5,Nil()))
scala> InvariantList.map(xs)(_ + 1)
res0: InvariantList[Int] = Cons(8,Cons(6,Nil()))

Related

Type inference with pattern binder at sing # in match case doesn't work as expected

Suppose Lofty is a sealed trait and Earthy is one of its case classes. In a match such as this:
loftyItem match {
...
case e # Earthy(x,y,z) => { foo(e) }
...
}
where foo expects an Earthy as argument, the compiler melts down because e is inferred only to be of type Lofty. I can work around this, but it doesn't fit my model of how things ought to be. (I'm working with Scala 2.13.5.) Can someone explain why the compiler's behavior makes sense, and make me happy with Scala again?
In response to comments, let me be more precise:
object QTest {
trait Ethereal
case class Lofty[A <: Ethereal](val slotMaybe: Option[A]) {
}
class Earthy(val field: Int) extends Ethereal
object Earthy {
def apply(fx: Int): Earthy = {
new Earthy(fx)
}
def unapply(x: Ethereal): Option[Int] = x match {
case y: Earthy => Some(y.field)
case _ => None
}
}
def testReally(just: Lofty[Ethereal]):
Lofty[Earthy] = {
just.slotMaybe match {
case Some(c) => c match {
case cnfC # Earthy(i) => {
just.copy(slotMaybe = Some(cnfC))
}
case _ => throw new RuntimeException("oops")
}
case _ => throw new RuntimeException("oops")
}
}
}
Compiling this produces the error:
QTest.scala:25: error: type mismatch;
found : QTest.Ethereal
required: QTest.Earthy
just.copy(slotMaybe = Some(cnfC))
I obviously jumped to a conclusion, but the full example seems to have the same issue. Why does the compiler infer type Ethereal for cnfC instead of Earthy? Even if the compiler gets the type right for most uses of #, why does it get it wrong here?
SLS 8.1.3 Pattern Binders states
A pattern p implies a type T if the pattern matches only values of the type T.
The pattern Earthy(i) in
case cnfC # Earthy(i) =>
represents extractor pattern meaning it will match according to your definition of unapply which is
object Earthy {
def unapply(x: Ethereal): Option[Int] = ???
}
Because the declared type of x is wider Ethereal instead of narrower Earthy it will not match
... only values of the type T
where T = Earthy, but instead it can match other subtypes of Ethereal as well. Hence the compiler can only be sure it will be some Ethereal.
If you wish to make it compile with the extractor pattern then either declare your unapply as
object Earthy {
def unapply(x: Earthy): Option[Int] = ???
}
or better yet use case class instead which gets a correct unapply automagically
case class Earthy(field: Int) extends Ethereal

Type parameter inference in pattern matching

I am trying to create some kind of heterogeneous map and came up with the following code
sealed trait Key[V]
case object LongKey extends Key[Long]
case class KeyValue[V](key: Key[V], value: V)
object KeyValue {
def values(key: Key[Long], kvs: List[KeyValue[_]]): List[Long] =
kvs.collect {
case KeyValue(LongKey, v) ⇒ v: Long // this compiles
case KeyValue(`key`, v) ⇒ v: Long // this doesn't
}
}
As Key is sealed and invariant I would have expected the 2 lines to be interchangeable.
Why does the second case not compile?
...: type mismatch;
found : _$1 where type _$1
required: Long
case KeyValue(`key`, v) ⇒ v: Long
Nice question. I initially thought this "should" work, but consider:
val kv: KeyValue[_] = KeyValue[String](null, "a")
val key: Key[Long] = null
kv match {
case KeyValue(`key`, v) => v // returns String, not Long!
}
Well, this is the way type system guaranty that your function is correct.
When you make your function signature,
def values(key: Key[Long], kvs: List[KeyValue[_]]): List[Long]
KeyValue[_] means KeyValue[Z] forSome { type Z }
When compiler does type inference, there is no way to make sure that the return would be List[Long], without constraining the case match to only KeyValue(LongKey, v).
If you need a generic function to collect the values, you could rewrite it as below.
def values[V](key: Key[V], kvs: List[KeyValue[V]]): List[V] =
kvs.collect {
case KeyValue(keyType, v) ⇒ v
}
On the other hand, if you want constrain them to only Long types, you need to change the function signature to,
def values(key: Key[Long], kvs: List[KeyValue[Long]]): List[Long]
That would allow you to pattern match on case KeyValue(key, v). Because compiler could verify that only Long type is to be expected.

Creating own List replica in 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
}

Scala: Fruitless Type Test Warning

I am trying to write a scala function to reverse a list. However, IDEA is highlighting the (warning) line saying "fruitless type test: a value of type ListDefinition.List[T] cannot also be a::[B]"
object ListDefinition {
def reverseList[T](list: List[T]) : List[T] = list match {
(warning) case x :: EmptyList => // something
case _ => // do somehting
}
abstract class List[+T]
case object EmptyList extends List[Nothing]
case class ConsList[T](value: T, next: List[T]) extends List[T]
The case statement needs to use the Cons constructor for your list:
case ConsList(x, EmptyList) => // something
The :: constructor is for Scala lists and will not work for yours.

forall in Scala

As shown below, in Haskell, it's possible to store in a list values with heterogeneous types with certain context bounds on them:
data ShowBox = forall s. Show s => ShowBox s
heteroList :: [ShowBox]
heteroList = [ShowBox (), ShowBox 5, ShowBox True]
How can I achieve the same in Scala, preferably without subtyping?
As #Michael Kohl commented, this use of forall in Haskell is an existential type and can be exactly replicted in Scala using either the forSome construct or a wildcard. That means that #paradigmatic's answer is largely correct.
Nevertheless there's something missing there relative to the Haskell original which is that instances of its ShowBox type also capture the corresponding Show type class instances in a way which makes them available for use on the list elements even when the exact underlying type has been existentially quantified out. Your comment on #paradigmatic's answer suggests that you want to be able to write something equivalent to the following Haskell,
data ShowBox = forall s. Show s => ShowBox s
heteroList :: [ShowBox]
heteroList = [ShowBox (), ShowBox 5, ShowBox True]
useShowBox :: ShowBox -> String
useShowBox (ShowBox s) = show s
-- Then in ghci ...
*Main> map useShowBox heteroList
["()","5","True"]
#Kim Stebel's answer shows the canonical way of doing that in an object-oriented language by exploiting subtyping. Other things being equal, that's the right way to go in Scala. I'm sure you know that, and have good reasons for wanting to avoid subtyping and replicate Haskell's type class based approach in Scala. Here goes ...
Note that in the Haskell above the Show type class instances for Unit, Int and Bool are available in the implementation of the useShowBox function. If we attempt to directly translate this into Scala we'll get something like,
trait Show[T] { def show(t : T) : String }
// Show instance for Unit
implicit object ShowUnit extends Show[Unit] {
def show(u : Unit) : String = u.toString
}
// Show instance for Int
implicit object ShowInt extends Show[Int] {
def show(i : Int) : String = i.toString
}
// Show instance for Boolean
implicit object ShowBoolean extends Show[Boolean] {
def show(b : Boolean) : String = b.toString
}
case class ShowBox[T: Show](t:T)
def useShowBox[T](sb : ShowBox[T]) = sb match {
case ShowBox(t) => implicitly[Show[T]].show(t)
// error here ^^^^^^^^^^^^^^^^^^^
}
val heteroList: List[ShowBox[_]] = List(ShowBox(()), ShowBox(5), ShowBox(true))
heteroList map useShowBox
and this fails to compile in useShowBox as follows,
<console>:14: error: could not find implicit value for parameter e: Show[T]
case ShowBox(t) => implicitly[Show[T]].show(t)
^
The problem here is that, unlike in the Haskell case, the Show type class instances aren't propagated from the ShowBox argument to the body of the useShowBox function, and hence aren't available for use. If we try to fix that by adding an additional context bound on the useShowBox function,
def useShowBox[T : Show](sb : ShowBox[T]) = sb match {
case ShowBox(t) => implicitly[Show[T]].show(t) // Now compiles ...
}
this fixes the problem within useShowBox, but now we can't use it in conjunction with map on our existentially quantified List,
scala> heteroList map useShowBox
<console>:21: error: could not find implicit value for evidence parameter
of type Show[T]
heteroList map useShowBox
^
This is because when useShowBox is supplied as an argument to the map function we have to choose a Show instance based on the type information we have at that point. Clearly there isn't just one Show instance which will do the job for all of the elements of this list and so this fails to compile (if we had defined a Show instance for Any then there would be, but that's not what we're after here ... we want to select a type class instance based on the most specific type of each list element).
To get this to work in the same way that it does in Haskell, we have to explicitly propagate the Show instances within the body of useShowBox. That might go like this,
case class ShowBox[T](t:T)(implicit val showInst : Show[T])
val heteroList: List[ShowBox[_]] = List(ShowBox(()), ShowBox(5), ShowBox(true))
def useShowBox(sb : ShowBox[_]) = sb match {
case sb#ShowBox(t) => sb.showInst.show(t)
}
then in the REPL,
scala> heteroList map useShowBox
res7: List[String] = List((), 5, true)
Note that we've desugared the context bound on ShowBox so that we have an explicit name (showInst) for the Show instance for the contained value. Then in the body of useShowBox we can explicitly apply it. Also note that the pattern match is essential to ensure that we only open the existential type once in the body of the function.
As should be obvious, this is a lot more vebose than the equivalent Haskell, and I would strongly recommend using the subtype based solution in Scala unless you have extremely good reasons for doing otherwise.
Edit
As pointed out in the comments, the Scala definition of ShowBox above has a visible type parameter which isn't present in the Haskell original. I think it's actually quite instructive to see how we can rectify that using abstract types.
First we replace the type parameter with an abstract type member and replace the constructor parameters with abstract vals,
trait ShowBox {
type T
val t : T
val showInst : Show[T]
}
We now need to add the factory method that case classes would otherwise give us for free,
object ShowBox {
def apply[T0 : Show](t0 : T0) = new ShowBox {
type T = T0
val t = t0
val showInst = implicitly[Show[T]]
}
}
We can now use plain ShowBox whereever we previously used ShowBox[_] ... the abstract type member is playing the role of the existential quantifier for us now,
val heteroList: List[ShowBox] = List(ShowBox(()), ShowBox(5), ShowBox(true))
def useShowBox(sb : ShowBox) = {
import sb._
showInst.show(t)
}
heteroList map useShowBox
(It's worth noting that prior to the introduction of explict forSome and wildcards in Scala this was exactly how you would represent existential types.)
We now have the existential in exactly the same place as it is in the original Haskell. I think this is as close to a faithful rendition as you can get in Scala.
The ShowBox example you gave involves an existential type. I'm renaming the ShowBox data constructor to SB to distinguish it from the type:
data ShowBox = forall s. Show s => SB s
We say s is "existential", but the forall here is a universal quantifier that pertains to the SB data constructor. If we ask for the type of the SB constructor with explicit forall turned on, this becomes much clearer:
SB :: forall s. Show s => s -> ShowBox
That is, a ShowBox is actually constructed from three things:
A type s
A value of type s
An instance of Show s.
Because the type s becomes part of the constructed ShowBox, it is existentially quantified. If Haskell supported a syntax for existential quantification, we could write ShowBox as a type alias:
type ShowBox = exists s. Show s => s
Scala does support this kind of existential quantification and Miles's answer gives the details using a trait that consists of exactly those three things above. But since this is a question about "forall in Scala", let's do it exactly like Haskell does.
Data constructors in Scala cannot be explicitly quantified with forall. However, every method on a module can be. So you can effectively use type constructor polymorphism as universal quantification. Example:
trait Forall[F[_]] {
def apply[A]: F[A]
}
A Scala type Forall[F], given some F, is then equivalent to a Haskell type forall a. F a.
We can use this technique to add constraints to the type argument.
trait SuchThat[F[_], G[_]] {
def apply[A:G]: F[A]
}
A value of type F SuchThat G is like a value of the Haskell type forall a. G a => F a. The instance of G[A] is implicitly looked up by Scala if it exists.
Now, we can use this to encode your ShowBox ...
import scalaz._; import Scalaz._ // to get the Show typeclass and instances
type ShowUnbox[A] = ({type f[S] = S => A})#f SuchThat Show
sealed trait ShowBox {
def apply[B](f: ShowUnbox[B]): B
}
object ShowBox {
def apply[S: Show](s: => S): ShowBox = new ShowBox {
def apply[B](f: ShowUnbox[B]) = f[S].apply(s)
}
def unapply(b: ShowBox): Option[String] =
b(new ShowUnbox[Option[String]] {
def apply[S:Show] = s => some(s.shows)
})
}
val heteroList: List[ShowBox] = List(ShowBox(()), ShowBox(5), ShowBox(true))
The ShowBox.apply method is the universally quantified data constructor. You can see that it takes a type S, an instance of Show[S], and a value of type S, just like the Haskell version.
Here's an example usage:
scala> heteroList map { case ShowBox(x) => x }
res6: List[String] = List((), 5, true)
A more direct encoding in Scala might be to use a case class:
sealed trait ShowBox
case class SB[S:Show](s: S) extends ShowBox {
override def toString = Show[S].shows(s)
}
Then:
scala> val heteroList = List(ShowBox(()), ShowBox(5), ShowBox(true))
heteroList: List[ShowBox] = List((), 5, true)
In this case, a List[ShowBox] is basically equivalent to a List[String], but you can use this technique with traits other than Show to get something more interesting.
This is all using the Show typeclass from Scalaz.
I don't think a 1-to-1 translation from Haskell to Scala is possible here. But why don't you want to use subtyping? If the types you want to use (such as Int) lack a show method, you can still add this via implicit conversions.
scala> trait Showable { def show:String }
defined trait Showable
scala> implicit def showableInt(i:Int) = new Showable{ def show = i.toString }
showableInt: (i: Int)java.lang.Object with Showable
scala> val l:List[Showable] = 1::Nil
l: List[Showable] = List($anon$1#179c0a7)
scala> l.map(_.show)
res0: List[String] = List(1)
( Edit: Adding methods to show, to answer comment. )
I think you can get the same using implicit methods with context bounds:
trait Show[T] {
def apply(t:T): String
}
implicit object ShowInt extends Show[Int] {
def apply(t:Int) = "Int("+t+")"
}
implicit object ShowBoolean extends Show[Boolean] {
def apply(t:Boolean) = "Boolean("+t+")"
}
case class ShowBox[T: Show](t:T) {
def show = implicitly[Show[T]].apply(t)
}
implicit def box[T: Show]( t: T ) =
new ShowBox(t)
val lst: List[ShowBox[_]] = List( 2, true )
println( lst ) // => List(ShowBox(2), ShowBox(true))
val lst2 = lst.map( _.show )
println( lst2 ) // => List(Int(2), Boolean(true))
Why not:
trait ShowBox {
def show: String
}
object ShowBox {
def apply[s](x: s)(implicit i: Show[s]): ShowBox = new ShowBox {
override def show: String = i.show(x)
}
}
As the authorities' answers suggested,
I'm often surprised that Scala can translate "Haskell type monsters" into very simple one.