Extension methods in typeclasses are surprising

On that note, has anyone figured out how to implement something like 1.pure[List] as an extension method?

I tried a couple different variants, but nothing worked.

trait Pure[F[_]] {
  def pure[A](a: A): F[A]
  
  extension [A] (a: A) def pureOne: F[A] = pure(a)
}
object Pure {
  given Pure[List] {
    def pure[A](a: A): List[A] = a :: Nil
  }
  
  extension [A,F[_]: Pure] (a: A)  def pureTwo: F[A] = summon[Pure[F]].pure(a)
  
  final class PartiallyAppliedPureThree[A](val a: A) extends AnyVal {
    def apply[F[_]: Pure] = summon[Pure[F]].pure(a)
  }
    
  extension [A] (a: A) def pureThree: PartiallyAppliedPureThree[A] = new PartiallyAppliedPureThree[A](a)      
}

def trialOne() = {
  // Without the explicit import of givens, fails with:
  // "value pureE is not a member of Int"
  import Pure.{given _}
  
  // Fails with:
  // value pureE is not a member of Int.
  // An extension method was tried, but could not be fully constructed:
  //
  //     Pure.given_Pure_List.extension_pureE[List](1)
  //println(1.pureOne[List])
  
  // Fails with:
  // Found:    (1 : Int)
  // Required: List
  //println(Pure.given_Pure_List.extension_pureOne[List](1))
}

def trialTwo() = {
  // Without explict import of method, fails with:
  // value pureTwo is not a member of Int
  import Pure.pureTwo
  
  // Fails with:
  // value pureTwo is not a member of Int.
  // An extension method was tried, but could not be fully constructed:
  //
  //     Pure.extension_pureTwo[List](1)
  //println(1.pureTwo[List])
  
  // Works if called explicitly with explicit type parameters
  println(Pure.extension_pureTwo[Int,List](1))
}

def trialThree() = {
  // Without explicit import of method, fails with:
  // value pureThree is not a member of Int
  import Pure._
  
  // Fails with:
  // value pureThree is not a member of Int.
  // An extension method was tried, but could not be fully constructed:
  //
  //     Pure.extension_pureThree[List](1)
  //println(1.pureThree[List])
  
  // Fails with:
  // Found:    (1 : Int)
  // Required: List
  // println(Pure.extension_pureThree[List](1))
  
  // Works, if called explicity with the type parameter second
  println(Pure.extension_pureThree(1)[List])
  // More explicit version of the preceding call
  println(Pure.extension_pureThree(1).apply[List])
  
  // Also works
  println(1.pureThree.apply[List])
}

@main
def run(): Unit = {
  trialOne()
  trialTwo()
  trialThree()
}
1 Like