Search code examples
scalatypesmonadsscala-3subtyping

Scala 3. Defining extension for class + all subclasses


Learning Scala 3.

below code

// monad defined for Option
  given Monad3[Option] with {
    def flatMap_[A, B](ma: Option[A])(fm: A => Option[B]): Option[B] = ???

    def return_[A](a: A): Option[A] = ???
  }

  extension[M[_] : Monad3, A] (cont: M[A])(using monad: Monad3[M]) {
    def flatMap_[B](f: A => M[B]): M[B] = monad.flatMap_(cont)(f)
  }

  extension[M[_] : Monad3, A] (contCont: M[M[A]])(using monad: Monad3[M]) {
    def join_(): M[A] = monad.join(contCont)
  }

  // test

  val flattenedOpt = Option(Option("hello!")).join_()
  println(flattenedOpt)

works for me.

Question: how do I modify signature of the helper extension for join so the below code with subtypes also works? Question is about Scala 3 syntax, not about monads, libraries, etc...

val flattenedOpt = Some(Some("hello!")).join_()

Is it smth with type bounds?

thanks


Solution

  • Question is about Scala 3 syntax, not about monads, libraries, etc...

    Well, the thing is that the question is exactly about monads, not Scala 3 syntax etc. You can define separate instances of Monad for Option and Some

    given Monad3[Option] with
      def flatMap_[A, B](ma: Option[A])(fm: A => Option[B]): Option[B] = ma match
        case Some(a) => fm(a)
        case None => None
      def return_[A](a: A): Option[A] = Some(a)
    
    given Monad3[Some] with
      def flatMap_[A, B](ma: Some[A])(fm: A => Some[B]): Some[B] = fm(ma.value)
      def return_[A](a: A): Some[A] = Some(a)
    

    But generally you can't derive an instance for subtype or supertype from an instance for a type. That's because Monad can be only invariant with respect to M[_], not covariant (Monad[+M[_]]) or contravariant (Monad[-M[_]]).

    Calling generic function with Functor using subclass (cats/scalaz)

    Why can find `Functor` instance for Tree but not for Branch or Leaf?

    Cats instances for Some and None et al

    Can I get the compiler to realize there is an Applicative for the superclass?

    Also for some ADTs / GADTs (case classes, sealed-trait hierarchies) parametrized with a type constructor, generic derivation is possible for the type classes Functor, Traversable, Foldable but not for Monad or Applicative because there is no standard way to flatten arbitrary data types.

    Why there is no way to derive Applicative Functors in Haskell?

    Why is there no `-XDeriveApplicative` extension?

    Haskell - Automatic Monad instance


    If you're really asking just about Scala 3 syntax for type bounds, it's the same as in Scala 2 i.e. def foo[A <: B : TC] (for a type class TC[A]), def foo[A[x] <: B[x] : TC] (for a type class TC[A[_]]).

    You shouldn't write extension[M[_]: Monad3, A](cont: M[A])(using monad: Monad3[M]). It should be either implicit parameter extension[M[_], A](cont: M[A])(using monad: Monad3[M]) or context bound extension[M[_]: Monad3, A](cont: M[A]).