Search code examples
scalafunctional-programmingscala-3

Partially applied type constructor in Scala 3?


Reading "Learn You a Haskell for Great Good" and trying to understand how Haskell concepts of the book may be written in Scala 3.

chapter 11 mentions "partially applied type constructors" in section about Functions as Functors:

Another instance of Functor that we've been dealing with all along but didn't know was a Functor is (->) r. You're probably slightly confused now, since what the heck does (->) r mean? The function type r -> a can be rewritten as (->) r a, much like we can write 2 + 3 as (+) 2 3. When we look at it as (->) r a, we can see (->) in a slightly different light, because we see that it's just a type constructor that takes two type parameters, just like Either. But remember, we said that a type constructor has to take exactly one type parameter so that it can be made an instance of Functor. That's why we can't make (->) an instance of Functor, but if we partially apply it to (->) r, it doesn't pose any problems. If the syntax allowed for type constructors to be partially applied with sections (like we can partially apply + by doing (2+), which is the same as (+) 2), you could write (->) r as (r ->)

instance Functor ((->) r) where  
    fmap f g = (\x -> f (g x))  

I understand all this logic in the book + the fact that Haskell deals with type constructors just as with usual functions - those can be curried and partially applied.

Question:

What is Scala 3 analogue of such partially applied type constructor so we might define fmap in the way that visually resembles below Haskell definition?

(it is smth that can be modelled with higer-kinded types?)


Solution

  • In Scala 3 you can use type lambdas

    trait Functor[F[_]]:
      def map[A, B](f: A => B)(fa: F[A]): F[B]
    
    given [R]: Functor[[X] =>> R => X] with
      override def map[A, B](f: A => B)(fa: R => A): R => B = fa andThen f
    

    or kind projector (scalacOptions += "-Ykind-projector")

    given [R]: Functor[R => *] with
      override def map[A, B](f: A => B)(fa: R => A): R => B = fa andThen f
    

    Eventually this should become

    given [R]: Functor[R => _] with
      override def map[A, B](f: A => B)(fa: R => A): R => B = fa andThen f
    

    Polymorphic method works with type lambda, but not with type wildcard in Scala 3