In C++, I can create a variadic function template as follows:
#include <tuple>
// helper to loop over tuple
template <std::size_t I = 0, typename FuncT, typename... Args>
void for_each(std::tuple<Args...>& tuple, FuncT func) {
func(std::get<I>(tuple));
if constexpr (I + 1 < sizeof...(Args)) {
for_each<I + 1, FuncT, Args...>(tuple, func);
}
}
template <class A, class B, class Derived>
struct FBounded {
auto foo() { return static_cast<Derived *>(this); }
auto baz() { return static_cast<Derived *>(this); }
};
class Child1 : public FBounded<const char*, const char*, Child1> {};
class Child2 : public FBounded<bool, int, Child2> {};
class Child3 : public FBounded<double, const char*, Child3> {};
template <class... A, class... B, class... SubTypes>
static auto func(FBounded<A, B, SubTypes>... elems) {
auto args = std::tuple(elems...);
for_each(args, [](auto x) { x.foo()->baz(); });
}
int main() {
auto c1 = Child1();
auto c2 = Child2();
auto c3 = Child3();
func(c1, c2, c3);
}
I want to re-create this behavior in Scala. Here is what I have so far:
class FBounded[A, B, T <: FBounded[A, B, T]] {
def foo(): T = this.asInstanceOf[T]
def baz(): T = this.asInstanceOf[T]
}
class Child1 extends FBounded[Int, Double, Child1] {}
class Child2 extends FBounded[String, String, Child2] {}
class Child3 extends FBounded[Int, String, Child3] {}
def func(elems: Seq[FBounded[_, _, _]]) = {
elems.foreach(_.foo.baz)
}
val c1 = new Child1()
val c2 = new Child2()
val c3 = new Child3()
func(c1, c2, c3)
I receive the error:
error: value baz is not a member of _$3
elems.foreach(_.foo.baz)
^
I believe this has something to do with when Scala fills out the placeholder types, but I am not sure.
The
FBounded[_, _, _]
-type is a shortcut for an existential type that looks somewhat likeand for whatever reason, the compiler refuses to infer the correct f-bounds for type parameter
T
(at least I couldn't get it to do that).I guess that this could be connected in some way to the reason why existential types are dropped in Dotty.
Here is a workaround that simply avoids existential types altogether:
Instead of relying on the existential
FBounded[_, _, _]
, it uses a wrapper classFBE
that holds a long lists with all types and all the constraints. WithFBE
, thefunc
seems to work just fine:because it can be written out more explicitly as:
where we can use the explicit path dependent type
e.t
provided byFBE.t
for intermediate results.