For reasons beyond my control, my method receives inputs in the form of a tuple. This tuple should only contain instances of Foo
, i.e., it should look like (Foo, Foo ... Foo)
and should not have String
or Int
inside. I want to check this at compile-time instead of throwing an exception at runtime. How can I achieve this?
Below is the code I currently have, which is not right:
def f(tupleOfFoos: Tuple): Tuple = {
for (x <- tupleOfFoos) assert(x.isInstanceOf[Foo])
mapTuple(tupleOfFoos, irrelevantFunction)
}
I am open to using Shapeless or the new features introduced in Dotty/Scala 3.
In Scala 2, with Shapeless, you can do this (Scastie):
LiftAll
ensures there's an instance ofFoo =:= X
for everyX
inH
, andgen
makes sure thatT
andH
are not completely unrelated types.In Dotty, you can add an evidence parameter with a match type for this:
This will allow you to call
f((Foo(), Foo(), Foo()))
but notf((1, 2, 3))
.Homogenous
is a recursive match type with a base case ofEmptyTuple
. If a tuple is empty, then it's not filled with non-Foo
s, so the type becomesDummyImplicit
, which has an implicit already in scope. Otherwise, we check if it looks like(H, ...)
/H *: t
, in which case we need to check if the rest of the tuple (t
) is also valid. If it doesn't match that second case, we know the tuple is invalid, in which case the result isNothing
, which sane people don't make implicit values of.If you want to use context bounds, you can make an additional curried type (Scastie):
Unfortunately, I haven't been able to get it to work with a single curried type (Scastie):