Can I replace F[_] with F[T] when using type constructor?

trait Monad[F[_], A] { // higher-kinded type class
    def flatMap[B](f: A => F[B]): F[B]
    def map[B](f: A => B): F[B]
  }

hi, in the code above, can I use Monad[F[T], A] to replace the Monad[F[_], A]? I tried in AMM, the compiler didn’t complain, and I think they are the same, but I am not sure.
hope anybody could explain a little.

1 Like

I’m somewhat surprised that this is actually valid syntax, but yeah, it looks like you can use any “free” identifier in place of the underscore.

$ cs launch scala:2.13.8
scala> trait Monad[F[_]]
scala> :kind -v Monad
Monad's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.
scala> trait MonadT[F[T]]
scala> :kind -v MonadT
MonadT's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.

I’d stick to the underscore, though. I’d think it’s more idiomatic, and it’s just the point that this is never used/referenced anywhere, but only filled in with a more specific type reference, so why bother giving it a name?

Note that with a type class, you separate the Monad concept from the type “having” a Monad. A full instance of the underlying type should be passed into the Monad, there should be no notion of the nested type A attached to the Monad type itself.

trait Monad[F[_]]:
  def pure[A](a: A): F[A]
  def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
3 Likes

thanks, I have another question:
here is another type constructor:
trait M1[F[F[_]]]
and there is another type constructor:

trait M2[F[G[_]]]

are they the same? I want to build a type constructor for the container like List[List[Int]], how to abstract this type?

thanks a lot.

Just ask for the constructor itself and the methods refer to the nested.

Like

trait Nested[F[_]] {
  def foo[T](fft: F[F[T]])
}

thanks.
if I defined two traits like below:

trait M1[F[F[_]]]

trait M2[F[G[_]]]

are they the same?

A quick experiment seems to show that they are the same; i.e. the inner identifier doesn’t refer to the outer one (the actual type), is just the syntax to specify the kind.

Which also means that they probably won’t do what you want.
Neither of them can be used with things like:

  • [X] =>> List[X]
  • [X] =>> List[List[X]]
  • [X] =>> List[Set[X]]

Rather, it can be used with things like:

  • Funtor
  • Monad
  • MonoidK

i.e. things that where defined like:

trait Foo[F[_]]
1 Like

hello, now that neither of them can be used with things above, why the compiler gets through? what does this definition( " trait M1[F[G[_]]] " ) mean ? can you give some examples for this kind of definition? thanks very much again.

as I tried, trait M1[F[G[_]]] equals the definition of trait M1[F[_[_]]]
but when will this definition been used now that the 3 examples above like [Int]=>>List[List[X]] can not be used in this definition.

It means that M1 is a type of kind ((* -> *) -> *) -> *
Meaning, it has a type parameter of kind (* -> *) -> *
Which means that to create a value of M1 you need to provide a type of that kind.

Like Monad which is defined like Monad[F[_]]
Which means it has kind (* -> *) -> *
Which is the kind M1 needs.

If you look closely to the syntax, it makes sense, Monad[F[_]] looks like F[G[_]]


Again, if you want to provide an interface that operates on values of shapes similar to List[List[Int]] all you need is this:

trait Nested[F[_]] {
  def foo[T](fft: F[F[T]])
}

thank you very much.
for the question above, I made a little code test as below:

trait M1[F[G[_]]]{
  def apply2[A](fft:F[List]):Unit
}

this works. but when I changed a little like below:

trait M1[F[G[_]]]{
  def apply2[A](fft:F[List[A]]):Unit
}

the compiler tells me:“Type argument List[A] does not have the same kind as its bound [_$2]” in scastie.

then I tried:
:kind -v F[List]
it says F[List] is a proper type.
so fft is a value,not a type constructor or type parameter. then how can I define a F[List] value?
can you explain something for me? thank you again.

Which is correct, because List[A] has kind * whereas F expects a type of kind * -> *

Right, because F has kind (* -> *) -> * thus, if you pass it List which has kind * -> * it returns a type of kind *; i.e. a proper type.

The very act of having a variable of such type, means that it is a value (variables can only contain values) it also means that such type is a proper type; i.e. a type of kind * -> *

Again, Functor and Monad are good examples of that.
For example,

trait TestF[F[_]] {
  def foo[A](a: A): F[A]
}

object TestList extends TestF[List] {
  override def foo[A](a: A): List[A] =
    a :: Nil
}

In that case, you can use TestList as a valid value for apply2 on an instance of M1[TestF]

Here the M1[TestF],do you mean the fft:F[List] I wrote in my code? and TestList can be a instance of F[List] and a valid parameter as fft:F[List]?
if so, I probably understand this example and the use of such a type constructor.
Thanks very much, especially for a new guy of scala type system like me.
Hope your reply again.

No.

Yes.

Let me show you an example, it may help.

trait TestG[F[_]] {
  def foo[A](a: A): F[A]
}

object TestGList extends TestG[List] {
  override def foo[A](a: A): List[A] =
    a :: Nil
}

trait M1[G[F[_]]]{
  def apply2(gList:G[List]):Unit
}

object M1TestG extends M1[TestG] {
  override def apply2(gList: TestG[List]): Unit = {
    println(gList.foo(3))
  }
}

M1TestG.apply2(TestGList)
// This would print: List(3)

Yeah, this is actually pretty advanced stuff, I wouldn’t bother too much with it right now.

thank you very much, this example helps me a lot.

I just wanna know something about the type system, but this feature really frustrates me a lot when first met. especially when it combines with those functor, applicatove and monad. wow. so intricate. but your explanation really helps. thanks again.