scalafunctional-programmingscala-catskind-projector

Difference between * (star) and _ (underscore) in type parameter


Here someone says that star is underscore from scala 3, but I've seen some code like this in scala 2.13:

def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...

Does it have a same meaning and just specify that type in * is not the same as in _?


Solution

  • _ denotes (depending on context)

    Here we want to understand the type constructor.

    Type constructor would be (simplifying) that F of something, that doesn't yet have that something defined, but we can apply A to it and make it a F[A]. E.g.

    Types with a "gap" are often denoted as * -> *, while types without them as *. We could read * simply as a type, while * -> * as "type that takes another type to form a type" - or a type constructor.

    (Higher-kinded types like one just mentioned are complex thing on its own, so it would be better for you to learn about them more outside of that question).

    * (from kind projector plugin) is used for kind projection - the syntax is inspired from the notation above to show where type would be passed if we wanted to create a new type:

    Monad[F[List[*]]]
    

    is really like:

    type UsefulAlias[A] = F[List[A]]
    Monad[UsefulAlias]
    

    except that it works without a type alias.

    If it was Dotty, it could be better expressed with a type lambda:

    // Monad[F[List[*]]] is equal to
    [A] =>> Monad[List[A]]
    

    In your example:

    def make[F[_]: ContextShift: MonadError[*[_], Throwable]: Effect: Logging](): ...
    

    It would probably be easier to read if it was written as:

    def make[F[_]: ContextShift: MonadError[*, Throwable]: Effect: Logging]():
    

    Thing is, that * would suggest that expected type is

    [A] =>> MonadError[A, Throwable]
    

    meanwhile kindness of * should be * -> * instead of *. So this *[_] means "we want to create a new type constructor here by making this thing in place of * a parameter, but we want to denote that this parameter is of kind * -> * instead of *

    [F[_]] =>> MonadError[F, Throwable]
    

    so we'll add [_] to show the compiler that it is a type constructor.

    It is quite a lot to absorb, and it should be easier, I can only feel sorry and say that in Dotty it will be clearer.