Scala 使用隐式模拟部分类型参数推断?

Scala 使用隐式模拟部分类型参数推断?,scala,Scala,我正在用Scala制作一个简单的依赖注入框架,用于构造函数注入。其思想是,对象可以像常规参数一样将其所需的服务放在构造函数中,并实现一个类型类,该类确定哪些参数来自容器,哪些参数在实例化时由用户传递 因此,它应该看起来像: trait Container { private singletons: Map[Class, AnyRef] def getSingleton[T: Manifest] = singletons(implicitly[Manifest[T]].erasur

我正在用Scala制作一个简单的依赖注入框架,用于构造函数注入。其思想是,对象可以像常规参数一样将其所需的服务放在构造函数中,并实现一个类型类,该类确定哪些参数来自容器,哪些参数在实例化时由用户传递

因此,它应该看起来像:

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
则从本地容器获得并传递给构造函数。其思想是,它应该为
Foo
获取
Constructor
type类的实例,并以一种类型安全的方式,知道它应该拥有的参数的数量和类型。还有,这是最难的部分,我不想写出参数的类型——只是要构造的对象

我试过两种方法:

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)

...
但是这种方法不起作用,因为为了“召唤”构造函数类型类实例,我们需要编写参数的类型,这是一大堆令人讨厌的样板文件:

construct[Foo, String]("asd") // yuck!
有没有一种方法可以使用类型类(或其他任何东西)对类型参数进行部分推断?我们在
constructor
实例定义中定义了
Foo
的构造函数参数类型,因此如果我们可以调用实例,我们应该能够调用
construct
并获得正确的参数类型。问题是在不必指定构造函数类型参数的情况下获取该实例。为此,我已经尝试了很多不同的想法,我觉得凭借Scala的强大功能和一大堆技巧,我必须找到一种方法来编写
构造[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中的类型参数推断是全有或全无的事情:如果显式地为类型参数块提供任何类型参数,则必须提供所有类型参数。因此,如果只想提供一组类型参数中的某些参数,则必须将它们安排为属于单独的类型参数块

在这种情况下,实现这一点的方法是将
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
方法,其中包含一个类型参数(可以推断),以及一个隐式参数(其类型由
T
a
确定)。您现在要进行的调用如下所示:

construct[Foo].apply("asd")  // T explicit, A inferred as String
Scala关于
apply
的语义糖化规则在这里适用,这意味着这可以重写为

construct[Foo]("asd")

这正是您想要的结果。

谢谢您的回复,我有点希望您能看看。。。对不起,问题太长了!不幸的是,这还不能完全解决问题,因为我无法使不同arg列表长度的重载工作。使用这种方法,我不得不说
construct1[Foo](“asd”)
construct2[Bar](“asd”,123)
,等等。我尝试添加一个隐式证据参数来区分
构造的重载(
构造[T](隐式ev:Constructor[T,u,u])
,但它似乎不起作用……我解决了它!诀窍是不要有一个
Construct1[T]
对象,而只有一个
Construct[T]
对象,它有所有的“应用”方法,每个参数列表长度一个。我会更新我的问题。谢谢你的帮助!这基本上与公认的答案(比它早两年)相同,只是这个答案甚至没有编译,也没有解释。
trait Construct[T, A] {
 def apply(arg: A): T
}

class Constructor[T]{
  def apply[A](arg : A)(implicit construct : Construct) = construct(arg)
}

object Constructor {
 def apply[T] = new Constructor[T]
}

Constructor[T]("arg")