Scala variable with multiple types - scala

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! :)

Related

How to create a generic function that can be applied to two one or more types containing the same parameters?

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.

Collection of Union types scala

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

scala: Parameterize by Type Union

I needed a type Union to force restriction on types, So as per answer in here, I defined my Union as:
sealed trait Value[T]
object Value{
implicit object NumberWitness extends Value[Int]
implicit object StringWitness extends Value[String]
}
Now, How do i create a list or class parameterized by this type union? Is it possible to do so?? I tried following syntax in repl, but without any luck:
scala> import Value._
import Value._
scala> def list[V: Value] = List("hello", 1)
list: [V](implicit evidence$1: Value[V])List[Any]
scala> list
<console>:18: error: ambiguous implicit values:
both object NumberWitness in object Value of type Value.NumberWitness.type
and object StringWitness in object Value of type Value.StringWitness.type
match expected type Value[V]
list
^
Or Is it possible to do so with advanced FP libraries like scalaz or cats??
This is called type class, not type union. And they are intended to allow you to write methods which work either with Int or with String, e.g.
def listOfValues[V: Value](x: V) = List(x)
listOfValues(1) // works
listOfValues("") // works
listOfValues(0.0) // doesn't work
listOfValues(1, "") // doesn't work
not to allow mixing different types.
You can do it using existential types, e.g.
case class WithValue[V: Value](x: V)
object WithValue {
implicit def withValue[V: Value](x: V) = WithValue(x)
}
def list = List[WithValue[_]]("hello", 1)
but I would not recommend actually doing that. There is quite likely a better way to solve your problem.
In particular, consider using simply
// not sealed if you need to add other types elsewhere
// can be Value[T] instead
sealed trait Value
case class IntValue(x: Int) extends Value
case class StringValue(x: Int) extends Value
// add implicit conversions to IntValue and StringValue if desired
List(StringValue("hello"), IntValue(1))

Is there a downside to using a tuple as the single parameter to a value class?

Scala 2.10 added support for value classes. One of the limitations of a value class is that it "must have only a primary constructor with exactly one public, val parameter whose type is not a value class."
Is there any downside to using a tuple for that single val parameter?
For example, I'd like to create a value type to represent a period of time with a start and an end. Because I can't have two parameters, I could represent that range as (Long, Long).
case class Period(timeRange: (Long, Long)) extends AnyVal {
def start: Long = timeRange._1
def end: Long = timeRange._2
def contains(time: Long): Boolean = time >= start && time < end
}
Would I still get the memory allocation benefit of value classes using this approach?
The downside is, as you've put it, that you will be creating an extra Tuple2 object to store the two Longs. There will be no boxing, however -- Tuple2 is specialized for Long.
So -- you are better off creating a case class in this case.
The following isn't directly related to your question, but may be useful. In some situations you need a typeclass if your value class is generic. For example, imagine that you want to add an additional factorial method ! for Numeric types. You would have to do the following:
implicit class IntegralOps[T: Numeric](val x: T) extends AnyVal {
def ! = ???
}
This won't work, because the typeclass constraint is translated into an additional implicit parameter and value classes only support one parameter:
implicit class IntegralOps[T](val x: T)(implicit $evidence: Numeric[T]) extends AnyVal {
def ! = ???
}
The trick you can get away with in such cases is to move the typeclass parameter to all the extension methods:
implicit class IntegralOps[T](val x: T) extends AnyVal {
def !(implicit $evidence: Numeric[T]) = ???
}
Since the same callsite is the same for the implicit conversion to the IntegralOps class and the call to the extension method, the same typeclass will apply in both cases.

Is it possible to refer to the types of Scala case class constructor arguments?

My goal is to create a trait that a case class can extend, which can process each constructor argument and then pass them as arguments to a method of the case class. All of the constructor arguments will be the same type with different type parameters, and the method will take arguments that match the type parameter of each constructor argument. You can think of it as a pre-processor of the constructor arguments. For example,
case class Foo(input1:Input[Int], input2:Input[String]) extends MagicTrait {
def run(input1:Int, input2:String) { ... }
}
Is this at all feasible? Is it feasible in a way that isn't terribly ugly (e.g. all reflection)? Is it possible to refer to the companion object of a case class in a way that is at all generic across case classes (e.g. a function that takes the output of Companion.unapply())?
Seeing as an acceptable solution allows the preprocessing functionality to be moved off the instances to an associated object the main remaining difficulty is that you want to be able to abstract over the arity and types (ie. the shape) of your case class constructors. This is possible with the HList implementation and polymorphic function values from shapeless.
First some preliminaries,
import shapeless.HList._
import shapeless.Functions._
import shapeless.Poly._
import shapeless.TypeOperators._
// Implementation of your Input wrapper
case class Input[T](value: T)
// Value extractor as a shapeless polymorphic function value
object value extends (Input ~> Id) {
def default[T](i : Input[T]) = i.value
}
We can now define a preprocessor base class which provides an apply method which takes an HList of Input types, maps the value polymorphic function across it (ie. performing the preprocessing) and then passes the resulting HList of non-Input types to the provided case class constructor (which is given in hlisted form, see below),
// Pre-processer base class
abstract class Preprocessor[In <: HList, Out <: HList, R](ctor : Out => R)
(implicit mapper : MapperAux[value.type, In, Out]) {
def apply(in : In) = ctor(in map value)
}
Now we define the case class with the post-processing component types,
case class Foo(input1 : Int, input2 : String)
and add one line of boilerplate,
object FooBuilder extends Preprocessor((Foo.apply _).hlisted)
(here the Foo companion object factory method is provided as the Preprocessor constructor argument in HListed form as required above.)
Now we can construct Foo instances using the FooBuilder.
val foo = FooBuilder(Input(23) :: Input("foo") :: HNil)
Unfortunately it isn't (currently) possible to combine the FooBuilder object with the Foo companion object: if you attempt to have the Foo companion extend Preprocessor you'll discover that the Foo factory method isn't available to be passed as the Preprocessor constructor argument.
To illustrate that this solution is really abstracting over type and arity, here's how we might add a second differently shaped case class,
case class Bar(input1 : Int, input2 : String, input3 : Boolean)
object BarBuilder extends Preprocessor((Bar.apply _).hlisted)
val bar = BarBuilder(Input(23) :: Input("foo") :: Input(true) :: HNil)
case class Input[T](value: T)
trait MagicTrait[T,U] {
val input1: Input[T]
val input2: Input[U]
def run: Unit
}
case class Foo(input1: Input[Int], input2: Input[String])
extends MagicTrait[Int, String] {
def run = println(input1.value * 2 + input2.value.toUpperCase)
}
scala> val m: MagicTrait[_,_] = Foo(Input(3), Input("hi"))
m: MagicTrait[_, _] = Foo(Input(3),Input(hi))
scala> m.run
6HI
edit:
If you want to find the types of the class parameters you can use the fact that case classes extend Product:
scala> Foo(2, "hi").productIterator.map(_.asInstanceOf[AnyRef].getClass).toList
res13: List[java.lang.Class[_]] =
List(class java.lang.Integer, class java.lang.String)
But this uses the reflection you wanted to avoid. This is why we use parameterization.
If you want to return its companion object, I'm not sure you can do this in a useful, type-safe way in the context of case classes, because companion objects don't extend an interface that specifies their extractor methods. You might be able to do something with structural types but it's probable that there's a better way to approach whatever problem it is that you're trying to solve.