0

I have the following code which works well.

package com.andrew

object ExperimentWithTypeConstraints {

  def union[T](t: T)(implicit c: (T =:= String)) = t match {
    case s: String => println(s"Some nice string: $s")
  }

  def main(args: Array[String]): Unit = {
    union("Hello.")
  }
}

Output:

Some nice string: Hello.

I would like to modify the definition of the method union something like this:

   def union[T](t: T)(implicit c: (T =:= String) Or (T =:= Int)) = t match {
    case s: String => println(s"Some nice string: $s")
    case i: Int => println(s"Some int: $i")
  }

The output for union(1) should be the following one: Some int: 1

Unfortunately, Scala does not know the logical operator Or (And, Not, ..) for such cases and hence it is not possible to compile it. How can I do it?

I found one solution at the following address (http://hacking-scala.org/post/73854628325/advanced-type-constraints-with-type-classes) but unfortunately I don't understand it well. Can you tell me how to solve this problem based on your approach? Or can you explain the solution from the above-mentioned url? Their solution works and it is the following:

@implicitNotFound("Argument does not satisfy constraints: ${A} Or ${B}")
trait Or[A, B]

object Or {
  private val evidence: Or[Any, Any] = new Object with Or[Any, Any]

  implicit def aExistsEv[A, B](implicit a: A) =
    evidence.asInstanceOf[Or[A, B]]

  implicit def bExistsEv[A, B](implicit b: B) =
    evidence.asInstanceOf[Or[A, B]]
}

I don't understand the part with Or. Or is an object. How can I combine this part (T =:= String) Or (T =:= Int) via Or together?

Thanks a lot, Andrew

Andrew_256
  • 229
  • 2
  • 6

2 Answers2

2

Unfortunately, Scala does not know the logical operator Or (And, Not, ..) for such cases and hence it is not possible to compile it. How can I do it?

Here's a different approach to the same problem. It is, admittedly, difficult to wrap your head around, but it works quite well.

trait Contra[-C]
type Union[A,B] = {
  type Check[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B]]
}

Usage:

def f[T: Union[String,Int]#Check](arg:T) = arg match {
  case s:String         => s"got string: ${s.toUpperCase}"
  case i:Int if i < 100 => s"got small Int: $i"
  case i:Int            => s"got bigger Int: $i"
}

Proof of concept:

f(119)            //res0: String = got bigger Int: 119
f("string time")  //res1: String = got string: STRING TIME
f(true)           //does not compile
jwvh
  • 50,871
  • 7
  • 38
  • 64
0

Lines

implicit def aExistsEv[A, B](implicit a: A) =
  evidence.asInstanceOf[Or[A, B]]

implicit def bExistsEv[A, B](implicit b: B) =
  evidence.asInstanceOf[Or[A, B]]

mean that if you implicitly have A then you implicitly have Or[A, B] and similarly if you implicitly have B then you implicitly have Or[A, B].

This is exactly how Or works in logic: if A is true then A Or B is true and similarly if B is true then A Or B is true.

And finally if T is String then implicit T =:= String is provided, and if T is Int then implicit T =:= Int is provided.

A X B is the same as X[A, B], so A Or B is the same as Or[A, B].

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Ok, thanks for your guick answer but I don't understand the way of the computation. How it is possible to use `Or[A,B]`? The definition of `Or` is the following: `object Or {..}`. There is no definition something like `object Or[A,B] {...}`. What type of construction it is? – Andrew_256 Mar 27 '18 at 19:11
  • `Or` is a [companion object](https://stackoverflow.com/questions/tagged/companion-object?sort=votes&pageSize=50) to trait `Or[A, B]`. `object Or[A,B] {...}` is not valid syntax, in Scala objects can't have generics. You could write `private def evidence[A, B](): Or[A, B] = new Or[A, B] {}` `implicit def aExistsEv[A, B](implicit a: A) = evidence[A, B]()` `implicit def bExistsEv[A, B](implicit b: B) = evidence[A, B]()`. You don't need to "use" `Or[A,B]`, it's enough that compiler knows that if there is an `A` or `B` then there is an `Or[A,B]`. – Dmytro Mitin Mar 27 '18 at 19:45
  • I don't actually understand question about "type of construction". [Implicits](https://stackoverflow.com/questions/tagged/implicits+scala)? [Type classes](https://stackoverflow.com/questions/tagged/typeclass+scala)? – Dmytro Mitin Mar 27 '18 at 19:45
  • Thanks again but I still don't understand the way of evaluation.The following row `implicit def aExistsEv[A, B](implicit a: A) = evidence.asInstanceOf[Or[A, B]]` should be evaluated as true/false because there is necessity to decide Or[A,B] as true/false,correct?But there is no `Boolean` value which should return true/false or something like this.The part `evidence.asInstanceOf[Or[A, B]]` converts the `evidence` to `Or[A, B]`, correct?If yes,I can't see any logical evaluation again.Moreover I don't see any method which launchs the method `aExistsEv` or `bExistsEv`.What magic is behind this? – Andrew_256 Mar 28 '18 at 10:46
  • No, the type is not `Boolean`, it's `Or[A, B]`. You should not be so literal. `Or[A, B]` is not Boolean, in some sense the type `Or[A, B]` behaves with respect to types `A, B` like boolean *A Or B* does for boolean *A, B*. The magic is implicits. Nobody needs to launch the methods. Are you familiar with implicits? – Dmytro Mitin Mar 28 '18 at 10:53
  • Not a lot but now I am studying the topic of implicit and I understand the implicit methods in that code (I hope) but I don't understand the part `(implicit a: A)` and `(implicit a: B)` in that code. Why does it work? There is no implicit variable (something like `implicit val nameOfVariable=...`).. Why it is necessary to have it there? and where does the compiler search this implicit parameter? How is it evaluated this concrete part? Thanks again. – Andrew_256 Mar 29 '18 at 15:33
  • `implicit def aExistsEv[A, B](implicit a: A): Or[A, B] =...` means that if compiler finds implicit `a` of type `A` then it knows that it has implicit of type `Or[A, B]`, namely `aExistsEv(a)` (`aExistsEv`is an [implicit conversion](https://docs.scala-lang.org/tour/implicit-conversions.html)). Implicits of types `T =:= String`, `T =:= Int` are provided automatically when `T` is `String`, `T` is `Int` respectively. – Dmytro Mitin Mar 29 '18 at 15:43
  • Class `=:=` https://github.com/scala/scala/blob/2.13.x/src/library/scala/Predef.scala#L516-L546 – Dmytro Mitin Mar 29 '18 at 17:22