gpt4 book ai didi

scala - 用隐式模拟部分类型参数推断?

转载 作者:行者123 更新时间:2023-12-04 23:27:19 25 4
gpt4 key购买 nike

我正在Scala中制作一个简单的依赖注入(inject)框架,用于构造函数注入(inject)。这个想法是 DI 的对象会将其所需的服务像常规参数一样放入其构造函数中,并实现一个类型类,该类确定它们的哪些参数是从容器中获取的,哪些是由用户在实例化时传递的。

所以,它应该看起来像:

trait Container {
private singletons: Map[Class, AnyRef]
def getSingleton[T: Manifest] =
singletons(implicitly[Manifest[T]].erasure).asInstanceOf[T]
... methods for adding singletons, etc ...
}

class Foo(arg: String, svc: FooService) {
...
}

trait Constructor[T] { ??? }

object FooConstructor extends Constructor[Foo] {
def construct(arg: String)(implicit container: Container) =
new Foo(arg, container.getSingleton[FooService])
}

现在基本上我希望能够有一个名为 construct 的方法。 ,我可以称之为 construct[Foo]("asd")并获得 Foo 的新实例与 "asd"传入构造函数, FooService从本地容器中获取并传递给构造函数。这个想法是它应该获取 Constructor 的实例。 Foo 的类型类并且以类型安全的方式,知道它应该具有的参数的数量和类型。另外,这是最难的部分,我不想写出参数的类型——只是要构造的对象。

我尝试了几件事:
trait Constructor1[T, A] {
def construct(arg: A): T
}

trait Constructor2[T, A1, A2] {
def construct(arg1: A1, arg2: A2): T
}

def construct[T, A](arg1: A): T = implicitly[Constructor1[T, A]].construct(arg1)

...

但是这种方法不起作用,因为它似乎是为了“召唤” Constructor类型类实例,我们需要编写参数的类型,这是很多讨厌的样板:
construct[Foo, String]("asd") // yuck!

有没有办法使用类型类(或其他任何东西)来部分推断类型参数?我们有 Foo 的构造函数参数的类型在 Constructor 中定义实例定义,所以如果我们可以调用实例,我们应该能够调用 construct并获得正确的参数类型。问题是无需指定构造函数类型参数即可获取该实例。我为此尝试了很多不同的想法,我觉得凭借 Scala 的强大功能和一系列技巧,我必须有一种方法可以编写 construct[Foo]("asd")并让参数列表是类型安全的。有任何想法吗?

更新:感谢 Miles Sabin 的出色回答 + 稍作修改,这是一种只需要一个类型参数并且适用于所有不同参数列表长度的方法。这是一种非常简单的方式来轻松地连接依赖关系,而无需反射成本:
trait Constructor1[T, A] { def construct(arg1: A)(implicit c: Container): T }
trait Constructor2[T, A, B] { def construct(arg1: A, arg2: B)(implicit c: Container): T }

implicit object FooConstructor extends Constructor1[Foo, String] {
def construct(arg1: String)(implicit c: Container) =
new Foo(arg1, c.getSingleton[FooService])
}

implicit object BarConstructor extends Constructor2[Bar, String, Int] {
def construct(arg1: String, arg2: Int)(implicit c: Container) =
new Bar(arg1, arg2, c.getSingleton[FooService])
}

class Construct[T] {
def apply[A](arg1: A)(implicit ctor: Constructor1[T, A], container: Container) =
ctor.construct(arg1)
def apply[A, B](arg1: A, arg2: B)(implicit ctor: Constructor2[T, A, B], container: Container) =
ctor.construct(arg1, arg2)
}

def construct[T] = new Construct[T]

construct[Foo]("asd")
construct[Bar]("asd", 123)

最佳答案

Scala 中的类型参数推断是全有或全无的事情:如果您为类型参数 block 显式提供任何类型参数,那么您必须全部提供它们。因此,如果您只想提供一组类型参数中的一部分,则必须安排它们属于单独的类型参数 block 。

在这种情况下,这样做的方法是拆分 construct方法分为两个阶段:第一个阶段,它接受一个显式类型参数并返回一个类似函数的值;第二个,它将类似函数的值应用于您希望为其推断类型的参数。

事情可能会这样,

// Function-like type
class Construct1[T] {
def apply[A](arg1: A)(implicit ctor : Constructor1[T, A]): T =
ctor.construct(arg1)
}

def construct[T] = new Construct1[T]

调用 construct[Foo] 的结果是 Construct1[Foo] 类型的值.这有一个 apply方法带有一个可以推断的类型参数和一个类型由两者确定的隐式参数 TA .您现在要进行的调用看起来像,
construct[Foo].apply("asd")  // T explicit, A inferred as String

Scala 的语义糖化规则围绕 apply在这里适用,这意味着这可以重写为,
construct[Foo]("asd")

这正是您想要的结果。

关于scala - 用隐式模拟部分类型参数推断?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10726222/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com