Is it possible in scala to have a collection of a union types. There are a few approaches to union types discussed here The top rated answer feels the most native, i have something like this:
sealed trait StringOrNumber[T]
object StringOrNumber {
implicit object IntWitness extends StringOrNumber[Int]
implicit object StringWitness extends StringOrNumber[String]
}
but when i try to make a map that contains both
val m: Map[String, Any] = Map("str" -> "hellp", "int" -> 32)
The scala compiler sees it as a map of [String,Any] Is there a way to tell the scala compiler this is a map [String, StringOrNumber]
Edit:
I dont think using the approach above is possible to create a collection of string or union. I think it needs to be another approach to a union type since the above is akin to overloaded methods rather than a true union type in the type system
The closest emulation of runtime union types, you can do in the current version of Scala, is to wrap types of the union in case classes extending some sealed trait. It's boilerplate-y and adds an extra wrapper layer over AnyRef types, but it works, it's better than just using Any, and you can also add implicit conversions from union types:
sealed trait StringOrNumber
object StringOrNumber {
case class IsNumber(i: Int) extends StringOrNumber
case class IsString(s: String) extends StringOrNumber
implicit def isNumber(i: Int): StringOrNumber = IsNumber(i)
implicit def isString(s: String): StringOrNumber = IsString(s)
}
Now you can define your Map:
scala> val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
m: Map[String,StringOrNumber] = Map(str -> IsString(hellp), int -> IsNumber(32))
Scala already has built-in case-classes, which are capable of representing arbitrary tagged disjoint unions of other types.
In your case, the simplest way to define StringOrNumber would be:
sealed trait StringOrNumber
case class Num(n: Int) extends StringOrNumber
case class Str(s: String) extends StringOrNumber
val m: Map[String, StringOrNumber] = Map(
"str" -> Str("hellp"),
"int" -> Num(42)
)
for ((k, v) <- m) {
v match {
case Num(n) => println("It's an int: " + n)
case Str(s) => println("A string: " + s)
}
}
If you don't want to create an extra trait for that, and if you have only two types, just use Either:
type StringOrNum = Either[String, Int]
Part of answer you copied is not complete. There are another part with match. And it shows that such kind of types union works in runtime.
So in general you mix two different things: compile time type union (which is also discussed in question you mentioned, originally written by Miles Sabin here) and which affects compiler checks, and runtime type check.
So, as soon as you use runtime approach, scala compiler just do not understand this union, and advice to use Any
You should write
val m: Map[String, StringOrNumber[_]] = ...
This feature is now under development in Dotty. As I know it would be like
Class[T1 | T2]
and
Class[T1 & T2]
but dotty would be available next years.
Now, you can use your approach, but it's a little tricky, and needs implicits.
You can also try Either type (only if you'll have 2 generic types), and you can also pay attention to scalaz library. It's all about type-level programming.
Did you try:
val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
You may also need to explicitly construct the StringOrNumber instances in this case to make it work.
Let me introduce you to my solution, using contravariance and type constraints:
//Add this to your util library
trait Contra[-A]
type Union[A,B] = Contra[A] <:< Contra[B]
//And see a usage example below
#implicitNotFound("Only Int or String can be sized")
type Sizeable[T] = Union[T, Int with String]
def sizeOf[T: Sizeable](sizeable: T): Int = {
sizeable match {
case i: Int => i
case s: String => s.length
}
}
Problem with this solution is that extends of Int or String would not be accepted here. Values entered here are checked constrained to being "Contravariant" to Int with String.
There is a way around this tho, you have to circumvent the type inference, and supply the base class in the type parameter, like this:
sizeOf[String](someExtendOfString)
Scala 3 has since been released with native support for union types.
type StringOrNumber = String | Int
Related
How can I define a generic tuple type that can
have variable length
contain different types
?
In the below example, I would need to define the return type of foo in trait A to match the implementations of the children objects.
trait A {
def foo: // how to define the return type here?
}
object O1 extends A {
def foo: (Int, String) = (123, "a string")
}
object O2 extends A {
def foo: ((String, Boolean), Int) = (("string inside a sub-tuple", true), 0, "another string")
}
Since this is Scala 2 you have two options.
1. Vanilla Scala
Using only vanilla Scala, you can do this:
trait A {
type O <: Product
def foo: O
}
object O1 extends A {
override final type O = (Int, String)
override def foo: (Int, String) =
(123, "a string")
}
This may work if you never want to abstract over A, but if you do then using Product is extremely unpleasant.
2. Shapeless
If you use Shapeless which means you may use HList instead of Product, that would make harder the use of a concrete implementation but easier an abstract usage (but still pretty unpleasant).
Anyways, I still recommend you to check if this is really the best solution to your meta-problem.
Let's say we have two case classes Woof & Meow
case class Meow(a: String, b: String)
case class Woof(a: String, b: String)
And we would like to create a function foo that concatenates a with b and works generically (kind-of) for both/either Woof & Meow like:
def foo[T](meowOrWoof: T) = meowOrWoof.a + meowOrWoof.b
Of course that will not compile as neither a nor b are parameters of T.
One thing I have tried, is to create a trait like:
trait Pets[T] {
def foo[T](someClass: T): String
}
case class Meow(a: String, b: String) extends Pets[Meow] {
override def foo[T](someClass: T) = a + b
}
case class Woof(a: String, b: String) extends Pets[Woof] {
override def foo[T](someClass: T) = a + b
}
But here we have to create the function multiple times even though it has the same implementation.
Is there another way we can create the function foo that works for both Meow & Woof, and concatenates a with b for either?
If your case classes can extend a common trait or abstract class...
abstract class AB {
val a:String
val b:String
}
case class Meow(a: String, b: String) extends AB
case class Woof(a: String, b: String) extends AB
def foo(ab:AB):String = ab.a + ab.b
foo(Woof("growl","bark")) //res0: String = growlbark
You can use structural type in scala:
Welcome to Scala 2.13.1 (OpenJDK 64-Bit Server VM, Java 1.8.0_222).
Type in expressions for evaluation. Or try :help.
scala> case class Meow(a: String, b: String)
defined class Meow
scala> case class Woof(a: String, b: String)
defined class Woof
scala> type WithAB = { def a: String; def b: String }
defined type alias WithAB
scala> def foo(ab: WithAB): String = ab.a + ab.b
scala> foo(Meow("hello, ", "world"))
res1: String = hello, world
scala> foo(Woof("hello, ", "scala"))
res2: String = hello, scala
This kind of thing can be achieved using the Shapeless library. Be aware however that this is a very advanced topic in Scala programming, and that Shapeless 3 will probably be quite different from the current 2.3.3 version, as it will be based on Scala 3 (Dotty).
It works as follows: you write a generic function that is parametrized by a type variable T which is going to be the case class that you want to read the a member of. In addition, you need to pass a bunch of implicits that tell you more about the structure of T:
it is a case class. This is represented by the LabelledGeneric typeclass
that this case class has a field named a. This is represented by the Selector typeclass
The LabelledGeneric allows you to convert back and forth between T and another representation of that type called Repr. The purpose is to have a uniform representation for all case classes, which makes it possible to write generic code. This Repr type is an HList, which is basically a tuple of arbitrary length.
The Selector implicit is what allows you to access individual fields of values of type Repr. You need one of these for each field you want to access, and the second type parameter of Selector is the name of the field. Unfortunately Shapeless was written before String singleton types were added to Scala in 2.13, which is why you can't write "a" but you have to write Witness.`'a`.T instead.
A generic function that will retrieve the String field a from an abitrary case class looks as follows:
import shapeless._
import shapeless.ops.record.Selector
def getA[T, Repr <: HList](t: T)(
implicit lg: LabelledGeneric.Aux[T, Repr],
aselector: Selector.Aux[Repr, Witness.`'a`.T, String]
): String = {
val repr: Repr = lg.to(t)
aselector(repr)
}
I've glossed over many of the details here (such as the "Aux pattern", which is needed to work around the fact that you can only have one implicit parameter list and thus can't use dependent types), so you'll have to do some more research into how shapeless works to completely understand it. The best resource I know of is this book:
https://books.underscore.io/shapeless-guide/shapeless-guide.html
Once you understand the above example, you should be able to work out how to implement the function you were trying to write.
I would like to define a List of elements implementing a common type class. E.g.
trait Show[A] {
def show(a: A): String
}
implicit val intCanShow: Show[Int] = new Show[Int] {
def show(int: Int): String = s"int $int"
}
implicit val stringCanShow: Show[String] = new Show[String] {
def show(str: String): String = str
}
The problem is, how to define a list = List(1, "abc") such that it is guaranteed that a Show instance for these values is in scope? I would then like to map this list over show like list map {_.show}.
I will first sketch a solution, and then explain why the naive approach with List[Any](1, "abc") cannot work.
What you can do
Define a wrapper class that can hold instances of type A together with instances of Show[A]:
case class Showable[A](a: A, showInst: Show[A]) {
def show: String = showInst.show(a)
}
Define your list as List[Showable[_]]:
var showableList: List[Showable[_]] = Nil
Maybe define a separate method to fill this list (consider packing the list itself and the builder-method in a class):
def addShowable[A: Show](a: A): Unit = {
showableList ::= Showable[A](a, implicitly[Show[A]])
}
Alternatively, you can carefully add a (very tightly scoped) implicit conversion:
implicit def asShowable[A](a: A)(implicit s: Show[A]): Showable[A] =
Showable(a, s)
and then costruct your list as follows (note the explicit type ascription):
val showableList = List[Showable[_]](1, "abc")
Now you can go through the list and call show:
showableList.map(_.show)
to obtain a list of String.
What you cannot do
You cannot simply define
val list: List[Any] = List(1, "abc", <showable3>, ..., <showableN>)
and then expect to be able to call show, because in order to call Show.show, you need actual Show instances. These things are not some type-hints that can be erased at runtime, they are actual objects, and they must be supplied by the compiler. Once you have created a List[Any], all is lost, because all the types are merged into an unexpressive upper bound Any, and the compiler has no way to inject all the necessary implicits Show[T_1],..., Show[T_N]. The argument is very similar to the third section "Dealing with implicits when defining interpreter for the Free monad" of this lengthy answer of mine.
An alternative way of handling this would be to use the shapeless library.
I would really reccommend this book which explains shapeless in a clear and concise manner.
Shapeless provides two things that I think will help you in this case:
Heterogeneous lists (HList)
Polymorphic functions to enable the HList mapping operation.
First import the required libraries (shapeless):
import shapeless.{HNil, Poly1, ::}
Create a heterogeneous list of whatever types you require. Note the type annotation is only there for clarity.
val data : Int :: String :: HNil = 1 :: "hello" :: HNil
Create a polymorphic function defining an implicit value for every type you require.
object Show extends Poly1 {
implicit def atT[T: Show] = at[T] (implicitly[Show[T]].show)
}
Shapeless provides an extension method for map on a HList to enable applying the show function to every element in the list
val result : String :: String :: HNil = data.map(Show)
Edited: thanks to #dk14 for the suggested improvement to the definition of the Show polymorphic function.
The core problem here is that you want to create a heterogenous list, something like List[Int, String] instead of List[Any]. This means you need a different structure that would preserve Int and String types, but still would be "mappable" like List. The one structure in scala-library that can contain heterogenous types is Tuple:
val tuple = (1, "abc")
val result = List(implicitly[Show[Int]].show(tuple._1), implicitly[Show[Int]].show(tuple._2))
However, scala-library can't map over tuples - you might want some syntax sugar for better readability.
So the obvious solution is HList from Shapeless: Int :: String :: HNil (or you can use tuple ops and stay with (Int, String))
import shapeless._
import poly._
//show is a polymorphic function
//think of it as `T => String` or even `(Show[T], T) => String`
object show extends Poly1 {
implicit def atT[T: Show] = at[T](implicitly[Show[T]].show)
}
# (1 :: "aaaa" :: HNil) map show
res8: String :: String :: HNil = "int 1" :: "aaaa" :: HNil
Or you could use at[Int]/at[String] instead of type-classes, like in #Steve Robinson's answer.
P.S. The lib could be found here. They also provide one-liner to get Ammonite REPL with shapeless integrated, so you could try my example out using:
curl -s https://raw.githubusercontent.com/milessabin/shapeless/master/scripts/try-shapeless.sh | bash
Notes:
Practically Shapeless solution requires as same amount of maintenance as Tuple-based one. This is because you have to keep track of your Int and String types anyways - you can never forget about those (unlike in homogenous List[T] case). All Shapeless does for you is nicer syntax and sometimes better type inference.
If you go with tuples - you can improve readability by using implicit class instead of Haskell-like style, or if you still want Haskell-like, there is a Simulacrum macro for better type-class syntax.
Given that other scala-library-only alternatives just capture type class instances inside some regular class, you could be better off with a regular OOP wrapper class:
trait Showable[T]{def value: T; def show: String}
class IntShow(val value: Int) extends Showable[Int]{..}
class StringShow(val value: String) extends Showable[String] {..}
val showables: List[Showable[_]] = List(new Showable(5), new Showable("aaa"))
showables.map(_.show)
Looks cleaner and more readable to me :)
If you like to rewrite dynamic dispatching in FP-style:
sealed trait Showable
final case class ShowableInt(i: Int) extends Showable
final case class ShowableString(s: String) extends Showable
implicit class ShowableDispatch(s: Showable){
def show = s match{ //use `-Xfatal-warnings` scalac option or http://www.wartremover.org/ to guarantee totality of this function
case ShowableInt(i) => ...
case ShowableString(s) => ...
}
}
List(ShowableInt(5), ShowableString("aaa")).map(_.show)
If you really want static dispatching (or ad-hoc polymorphism), given that other solutions introduce Showable[_] which is practically Showable[Any]:
case class Showable[T](v: T, show: String)
def showable(i: Int) = Showable(i, s"int $i")
def showable(s: String) = Showable(i, s)
List(showable(5), showable("aaa")).map(_.show)
Disclamer: I've made this answer to provide a solution to the concrete development problem, and not the theoretical problem of using typeclass
I would do it this way:
trait Showable{ def show(): String }
implicit class IntCanShow(int: Int) extends Showable {
def show(): String = s"int $int"
}
implicit class StringCanShow(str: String) extends Showable {
def show(): String = str
}
val l: List[Showable] = List(1,"asd")
Note that I changed the meaning of the trait Show, into Showable, such that the implementing classes are used as wrapper. We can thus simply require that we want Showable instances
(and because those classes are implicit, input of the List are automatically wrapped)
There is Either in scala which allow a variable to have 2 types value.
val x: Either[String, Int] = Left("apple")
However, I want to have more than 2 types for variable x e.g. {String, Int, Double, List[String] }.
e.g. val x:[type can be either String, Int, Double or List[String]]
//So that I can store either String, Int, Double, List[String] value in x.
Is there any way to achieve this?
IMO the most natural way to express this is to create an ADT (Algebraic Data Type):
sealed trait Foo
final case class Bar(s: String) extends Foo
final case class Baz(i: Int) extends Foo
final case class Fizz(d: Double) extends Foo
final case class Buzz(l: List[String]) extends Foo
And now you can pattern match on Foo:
val f: Foo = ???
f match {
case Bar(s) => // String
case Baz(i) => // Int
case Fizz(d) => // Double
case Buzz(l) => // List[String]
}
Look at shapeless coproducts
"shapeless has a Coproduct type, a generalization of Scala's Either to
an arbitrary number of choices"
Not sure what your exact use case is but check this out: scala type alias - how to have a type that represent multiple data types
Basically you create a trait represents an umbrella/parent class. Then you define multiple classes that extend the parent trait and put the parent trait as the expected parameter type in your function. Then you can pass any type that extends that parent trait.
You have to wrap the subtypes, but this allows you to have one type that represents multiple types.
Another way could be to use generic types and type bounds. These articles talk about these two subjects:
http://docs.scala-lang.org/tutorials/tour/variances.html
How do I setup multiple type bounds in Scala?
I'm still learning Scala, but hope this helps! :)
I have some code like this:
sealed trait Foo[A] {
def value: A
}
case class StringFoo(value: String) extends Foo[String]
case class IntFoo(value: Int) extends Foo[Int]
I'd like to have a function which can use the A type given a subtype's type parameter.
// Hypothetical invocation
val i: Int = dostuff[IntFoo](param)
val s: String = dostuff[StringFoo](param)
I can't figure out how to declare dostuff in a way that works. The closest thing I can figure out is
def dostuff[B <: Foo[A]](p: Param): A
But that doesn't work because A is undefined in that position. I can do something like
def dostuff[A, B <: Foo[A]](p: Param): A
But then I have to invoke it like dostuff[String, StringFoo](param) which is pretty ugly.
It seems like the compiler should have all the information it needs to move A across to the return type, how can I make this work, either in standard scala or with a library. I'm on scala 2.10 currently if that impacts the answer. I'm open to a 2.11-only solution if it's possible there but impossible in 2.10
Another option is to use type members:
sealed trait Foo {
type Value
def value: Value
}
case class StringFoo(value: String) extends Foo { type Value = String }
case class IntFoo(value: Int) extends Foo { type Value = Int }
def dostuff[B <: Foo](p: Any): B#Value = ???
// Hypothetical invocation
val i: Int = dostuff[IntFoo](param)
val s: String = dostuff[StringFoo](param)
Note that both solutions mainly work around the syntactic restriction in Scala, that you cannot fix one type parameter of a list and have the compiler infer the other.
As you might know, if you have a parameter of type Foo[A], then you can make the method generic in just A:
def dostuff[A](p: Foo[A]): A = ???
Since that might not always be the case, we can try to use an implicit parameter that can express the relationship between A and B. Since we can't only apply some of the generic parameters to a method call (generic parameter inference is all or nothing), we have to split this into 2 calls. This is an option:
case class Stuff[B <: Foo[_]]() {
def get[A](p: Param)(implicit ev: B => Foo[A]): A = ???
}
You can check the types in the REPL:
:t Stuff[IntFoo].get(new Param) //Int
:t Stuff[StringFoo].get(new Param) //String
Another option along the same lines, but using an anonymous class, is:
def stuff[B <: Foo[_]] = new {
def apply[A](p: Param)(implicit ev: B <:< Foo[A]): A = ???
}
:t stuff[IntFoo](new Param) //Int
Here, I've used apply in stead of get, so you can apply the method more naturally. Also, as suggested in your comment, here I've used <:< in the evidence type. For those looking to learn more about this type of generalized type constraint, you can read more here.
You might also consider using abstract type members instead of generic parameters here. When struggling with generic type inference, this often provides an elegant solution. You can read more about abstract type members and their relationship to generics here.