How to ensure a paramter is one type of a set of types?

Hi,

I have the following method:

def warnNotInteger[T <: W | H](msg: String, numerator: T, denominator: T, div: Int, remainder: Double) =
    if (remainder != 0.0) 
      C.warn(s"${msg} (${numerator}/${denominator} = ${div} + ${remainder})")

And I can call these as follows:

  val WIDTH: W = ... 
  val x_resolution: W = ... 
  val HEIGHT: H = ... 
  val y_resolution: H = ... 

   warnNotInteger("Number of vertical stripes not integer:",  WIDTH,  x_resolution, total_vertical,  wf)
    warnNotInteger("Number of horizontal stripes not integer:",HEIGHT, y_resolution, total_horizontal, hf)
    warnNotInteger("Number of horizontal stripes not integer:",HEIGHT, x_resolution, total_horizontal, hf)

How can one ensure the last call above does not compile? In other words T must either be an H or a W.

TIA

Provide type argument warnNotInteger[W]

1 Like

How the compiler “thinks”: if type parameters are not given explicitly, try fill in the type automatically by searching for most specific type that conforms - relax to more general types if needed and if no other type works try with most general type Any.

If you provide a type parameter, as recommended by @spamegg1, you “nail the type to the floor”, meaning that this will be a hard requirements on the type constraint problem that the compiler is trying to solve. If the problem is not solvable (i.e. you have provided inconsistent type constraints) then you will get a compile error. Which is good if this detects an error. Less bugs! THANK YOU compiler!

1 Like

The only way is to remove union at all. If this is not enough you should use type class pattern

def warnNotInteger[T](msg: String, numerator: T, denominator: T, div: Int, remainder: Double) =
    if (remainder != 0.0) 
      C.warn(s"${msg} (${numerator}/${denominator} = ${div} + ${remainder})")

Or you could use inheritance… (if it’s your own types, or wrap in your own types)

@spamegg1 Unfortunately I neglected to mention an important point: type W and H are of the same primitive type. I have declared these as opaque types. They server “only” to ensure that the correct parameters are used at the call site. My apologies.

@bjornregnell As I explained to @spamegg1, the problem is that H and W has the same primitive type. I will have to give some more thought to the use of inheritance. This may work although for more than 2 classes this may be convoluted.

@goshacodes Can you clarify? I can’t see how to get this to work.
TIA

@goshacodes Same issue as in the first suggestion. If W and H have the same type, no compilation error occurs. But you have reminded me of something.

Perhaps you could wrap them into something similar to:

def resolution(w: Width, h: Height): Option[(Int, Int)] = ???

trait Dim:
  case class Width(w: Double) extends Dim
  case class Height(h: Double) extends Dim

Or using enums:

enum Dim:
  case Width(w: Double)
  case Height(h: Double) 

Then I don’t understand what are you trying to achieve. If you want only different types to pass - remove type parameters and make your types exact :slight_smile:

1 Like

Thanks for all the suggestions. I came up with the “solution” below (single definition but multiple type parameters). It works with opaque types (no expensive wrapping) that use the same primitive type and fails with different primitive opaque types.

  def warnNotInteger[T1 <: W | H, T2 <: W | H](msg: String, numerator: T1, denominator: T2, div: Int, remainder: Double)
        (using T1 =:= T2) =
    if (remainder != 0.0) 
      C.warn(s"${msg} (${numerator}/${denominator} = ${div} + ${remainder})")

Important note: only works with opaque types and not simple type alias.

Thanks again.

1 Like