我有一个trait定义,当像这样实例化时包装副作用:
trait MyTrait[F[_]] {
def func1(param: Param): F[Param]
}
我的Param是一个case类,它本身接受这样的类型参数:
final case class Param[F[_]] {
field1: F[String],
field2: F[Int]
)
现在我的问题是,如果我将trait方法签名更改为以下内容,这意味着什么:
trait MyTrait[F[_]] {
def func1(param: Param[_]): F[Param[_]]
}
正如您所看到的,我在引用Param case类的任何地方都使用了通配符。这是一个好方法吗?我不想将我的接口绑定到方法参数上的类型期望。
正如@AndreyTyukin注意到的,你的代码无法编译,因为Param
和F
在种类上不一致
trait MyTrait[F[_]] {
def func1(param: Param): F[Param]
}
//compile error: class Param takes type parameters
final case class Param[F[_]](
field1: F[String],
field2: F[Int]
)
https://scastie.scala-lang.org/DmytroMitin/K2EHGDXERFCJisz45edMsA
也许你的意思是
trait MyTrait[F[_]] {
def func1(param: Param[F]): F[Param[F]]
}
final case class Param[F[_]](
field1: F[String],
field2: F[Int]
)
https://scastie.scala-lang.org/DmytroMitin/K2EHGDXERFCJisz45edMsA/1
func1
返回类型F[Param[F]]
看起来像固定点https://free.cofree.io/2017/11/13/recursion/
现在我的问题是,如果我将trait方法签名更改为以下内容,这意味着什么:
trait MyTrait[F[_]] { def func1(param: Param[_]): F[Param[_]] }
代替具有当前效果的Param[F]
F
,您开始使用具有任意(未知)效果的存在类型Param[_]
,可能与F
不同。
什么是存在类型?
这是一个好方法吗?我不想将我的接口绑定到方法参数上的类型期望。
取决于你的目标。对于您的设置,MyTrait
和Param
将具有不连接的效果,这有意义吗?
例如,其中一个正在写入数据库,而另一个正在写入磁盘上的文件。其中一个在进行时间旅行,而另一个在发射导弹。
如果您的设置确实有意义,可以使用不同的效果,请考虑修改签名,在方法级别上添加第二种效果类型(而不是存在)
trait MyTrait[F[_]] {
def func1[G[_]](param: Param[G]): F[Param[G]]
}
(还是应该是F[Param[F]]
?)或者G[Param[F]]
?这取决于你的设置)
或在类型-类级别
// (*)
trait MyTrait[F[_], G[_]] {
def func1(param: Param[G]): F[Param[G]]
}
或者你也可以试试
trait MyTrait[F[_]] {
def func1[G[_], H[_]](param: Param[G]): F[Param[H]]
}
或
trait MyTrait[F[_], G[_], H[_]] {
def func1(param: Param[G]): F[Param[H]]
}
在Scala的新问题类型参数和边界之后,我将在这里添加一个选项,即使G
成为抽象类型成员而不是方法的类型参数。那么G
必须在继承程序中实现,而不是方法必须为任意G
工作。
trait MyTrait[F[_]] {
type G[_]
def func1(param: Param[G]): F[Param[G]]
}
它类似于上面的(*),即让G
成为type-class的类型参数。