scalagenericstypeclassscala-catshigher-kinded-types

What is `F` in `F[_]: Sync` and where does it come from?


I would like to use the cats-saga from that repository: https://github.com/VladKopanev/cats-saga

However I am stuck on that piece of code at OrderSagaCoordinator.scala L160:

def apply[F[_]: Sync: Concurrent: Timer: Sleep: Parallel](
    paymentServiceClient: PaymentServiceClient[F],
    loyaltyPointsServiceClient: LoyaltyPointsServiceClient[F],
    orderServiceClient: OrderServiceClient[F],
    sagaLogDao: SagaLogDao[F],
    maxRequestTimeout: Int
  ): F[OrderSagaCoordinatorImpl[F]] =

What is F, where does it come from, can someone explain that piece of code ?

Thanks

Edit: I know what a generic type is. However in that case the apply method is called without specifying the concrete type and I do not see any places where it came from.

(for {
      paymentService <- PaymentServiceClientStub(randomUtil, clientMaxReqTimeout, flakyClient)
      loyaltyPoints  <- LoyaltyPointsServiceClientStub(randomUtil, clientMaxReqTimeout, flakyClient)
      orderService   <- OrderServiceClientStub(randomUtil, clientMaxReqTimeout, flakyClient)
      xa             = Transactor.fromDriverManager[IO]("org.postgresql.Driver", "jdbc:postgresql:Saga", "postgres", "root")
      logDao         = new SagaLogDaoImpl(xa)
      orderSEC       <- OrderSagaCoordinatorImpl(paymentService, loyaltyPoints, orderService, logDao, sagaMaxReqTimeout)

    // ...

Solution

  • Think of something concrete, say 'box of chocolates'

    case class Box(v: Chocolate)
    

    Now imagine we take away the chocolate, and make the box take any kind of element A, maybe box of coins, box of candy, box of cards, etc

    case class Box[A](v: A)
    

    Here we are polymorphic in the element type of the box. Many languages can express this level of polymorphism. But Scala takes this further. In the same way how we took away the chocolate, we can take away the box itself, essentially expressing a very abstract idea of "any kind of context of any type of elements"

    trait Ctx[F[_]]
    

    As another analogy consider the following

    box of chocolate  -> proper type                      -> case class Box(v: Chocolate)
    box of _          -> type constructor of first order  -> case class Box[A](v: A)
    _   of _          -> type constructor of higher order -> trait Ctx[F[_]]
    

    Now focus on _ of _. Here we have "something of something", which kind of seems like we have nothing. How can we do anything with this? This is where the idea of a type class comes into play. A type class can constrain a highly polymorphic shape such as F[_]

    def apply[F[_]: Sync](...)
    

    Here [F[_]: Sync] represents this constraint. It means that method apply accepts any type constructor of first kind for which there exists evidence that it satisfies the constraints of type class Sync. Note that type class Sync

    trait Sync[F[_]]
    

    is considered a higher order type constructor, whilst type parameter F[_] represents a first order type constructor. Similarly

    F[_] : Sync : Concurrent
    

    specifies that type constructor F must not only satisfy Sync constraints, but also constraints of Concurrent type class, and so on. These techniques are sometimes referred to as scary sounding

    higher order type constructor polymorphism

    and yet I am confident that most programmers have all the conceptual tools already present to understand it because

    1. if you ever passed a function to a function, then you can work with concept of higher order
    2. if you ever used a List, then you can work with concept of type constructors
    3. if you ever wrote a method that uses the same implementation for both Integers and Doubles, then you can work with concept of polymorphism

    Providing evidence that a type constructor satisfies constraints of a type class are given using Scala's implicit mechanisms. IMO Scala 3 has significantly simplified the concept so consider https://dotty.epfl.ch/docs/reference/contextual/type-classes.html