Scala 可变数量的参数是 F-Bounded 类型的 sub-类
Scala variable number of parameters that are sub-classes of an F-Bounded type
在 C++ 中,我可以创建一个可变函数模板,如下所示:
#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);
}
我想在 Scala 中重新创建此行为。这是我目前所拥有的:
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)
我收到错误:
error: value baz is not a member of _
elems.foreach(_.foo.baz)
^
我认为这与 Scala 填充占位符类型时有关,但我不确定。
FBounded[_, _, _]
类型是存在类型的快捷方式,看起来有点像
FBounded[A, B, T] forSome { type A; type B; type T <: FBounded[A, B, T] }
并且出于某种原因,编译器拒绝为类型参数 T
推断正确的 f 边界(至少我无法做到这一点)。
我想这可能与 existential types are dropped in Dotty.
的原因有某种联系
这是一个完全避免存在类型的解决方法:
class FBounded[A, B, T <: FBounded[A, B, T]] {
self: T =>
def foo: T = self
def baz: T = self
def wrap: FBE = new FBE {
type a = A
type b = B
type t = T
val value: t = self
}
}
class Child1 extends FBounded[Int, Double, Child1] {}
class Child2 extends FBounded[String, String, Child2] {}
class Child3 extends FBounded[Int, String, Child3] {}
/** Wrapper for FBounded existential types */
abstract class FBE {
type a
type b
type t <: FBounded[a, b, t]
val value: t
}
def func(elems: FBE*) = {
elems.map(_.value.foo.baz)
}
val c1 = new Child1()
val c2 = new Child2()
val c3 = new Child3()
func(c1.wrap, c2.wrap, c3.wrap)
它不依赖于存在的 FBounded[_, _, _]
,而是使用包装器 class FBE
来保存包含所有类型和所有约束的长列表。使用 FBE
,func
似乎工作得很好:
def func(elems: FBE*) = {
elems.map(_.value.foo.baz)
}
因为可以更明确地写成:
def funcMoreExplicit(elems: FBE*) = {
elems.map(e => {
val v: e.t = e.value
val fooRes: e.t = v.foo
val bazRes: e.t = fooRes.baz
bazRes
})
}
我们可以使用 FBE.t
提供的显式路径依赖类型 e.t
作为中间结果。
在 C++ 中,我可以创建一个可变函数模板,如下所示:
#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);
}
我想在 Scala 中重新创建此行为。这是我目前所拥有的:
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)
我收到错误:
error: value baz is not a member of _
elems.foreach(_.foo.baz)
^
我认为这与 Scala 填充占位符类型时有关,但我不确定。
FBounded[_, _, _]
类型是存在类型的快捷方式,看起来有点像
FBounded[A, B, T] forSome { type A; type B; type T <: FBounded[A, B, T] }
并且出于某种原因,编译器拒绝为类型参数 T
推断正确的 f 边界(至少我无法做到这一点)。
我想这可能与 existential types are dropped in Dotty.
的原因有某种联系这是一个完全避免存在类型的解决方法:
class FBounded[A, B, T <: FBounded[A, B, T]] {
self: T =>
def foo: T = self
def baz: T = self
def wrap: FBE = new FBE {
type a = A
type b = B
type t = T
val value: t = self
}
}
class Child1 extends FBounded[Int, Double, Child1] {}
class Child2 extends FBounded[String, String, Child2] {}
class Child3 extends FBounded[Int, String, Child3] {}
/** Wrapper for FBounded existential types */
abstract class FBE {
type a
type b
type t <: FBounded[a, b, t]
val value: t
}
def func(elems: FBE*) = {
elems.map(_.value.foo.baz)
}
val c1 = new Child1()
val c2 = new Child2()
val c3 = new Child3()
func(c1.wrap, c2.wrap, c3.wrap)
它不依赖于存在的 FBounded[_, _, _]
,而是使用包装器 class FBE
来保存包含所有类型和所有约束的长列表。使用 FBE
,func
似乎工作得很好:
def func(elems: FBE*) = {
elems.map(_.value.foo.baz)
}
因为可以更明确地写成:
def funcMoreExplicit(elems: FBE*) = {
elems.map(e => {
val v: e.t = e.value
val fooRes: e.t = v.foo
val bazRes: e.t = fooRes.baz
bazRes
})
}
我们可以使用 FBE.t
提供的显式路径依赖类型 e.t
作为中间结果。