Home > Net >  Scala Higher Kinded Types for Traits and Method Parameters
Scala Higher Kinded Types for Traits and Method Parameters

Time:12-08

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.

CodePudding user response:

As @AndreyTyukin noticed, your code doesn't compile because Param and F don't agree in kinds

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

Maybe you meant

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 return type F[Param[F]] looks like fix point https://free.cofree.io/2017/11/13/recursion/

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[_]]
}

Instead of Param[F] with current effect F you're starting to use an existential type Param[_] with arbitrary (unknown) effect, possibly different from F.

What is an existential type?

Is this a good approach? I do not want to tie my interface to a type expectation on a method param.

Depends on your goal. Does it make sense for your setting that MyTrait and Param will 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

trait MyTrait[F[_]] {
  def func1[G[_]](param: Param[G]): F[Param[G]]
}

(Or should it be still F[Param[F]]? This depends on your setting.)

or on type-class level

trait MyTrait[F[_], G[_]] {
  def func1(param: Param[G]): F[Param[G]]
}

Or you can even try :)

trait MyTrait[F[_]] {
  def func1[G[_], H[_]](param: Param[G]): F[Param[H]]
}

or

trait MyTrait[F[_], G[_], H[_]] {
  def func1(param: Param[G]): F[Param[H]]
}
  • Related