I have a trait definition that wraps side effects when instantiated like this:
trait MyTrait[F[_]] {
def func1(param: Param): F[Param]
}
Where my Param is a case class which itself takes type parameters like this:
final case class Param[F[_]] {
field1: F[String],
field2: F[Int]
)
Now my question is, what does it mean if I change my trait method signature to the following:
trait MyTrait[F[_]] {
def func1(param: Param[_]): F[Param[_]]
}
As you can see I'm having a wildcard everywhere I reference the Param case class. Is this a good approach? I do not want to tie my interface to a type expectation on a method param.
As @AndreyTyukin noticed, your code doesn't compile because
ParamandFdon't agree in kindshttps://scastie.scala-lang.org/DmytroMitin/K2EHGDXERFCJisz45edMsA
Maybe you meant
https://scastie.scala-lang.org/DmytroMitin/K2EHGDXERFCJisz45edMsA/1
func1return typeF[Param[F]]looks like fix point https://free.cofree.io/2017/11/13/recursion/Instead of
Param[F]with current effectFyou're starting to use an existential typeParam[_]with arbitrary (unknown) effect, possibly different fromF.What is an existential type?
Depends on your goal. Does it make sense for your setting that
MyTraitandParamwill have unconnected effects?For example one of them is going to database while the other is writing to a file on disc. One of them is travelling through time while the other is launching missiles.
If this really makes sense for your setting to work with different effects, consider modifying the signature adding the 2nd effect type (rather than existential) on method level
(Or should it be still
F[Param[F]]? OrG[Param[F]]? This depends on your setting.)or on type-class level
Or you can even try
or
After new question Type Arguments and Bounds in Scala I'll add here that one more option is to make
Gan abstract type member rather than method's type parameter. ThenGmust be implemented in inheritors rather than the method must work for arbitraryG.It's similar to the above (*), i.e. having
Ga type parameter of the type-class.