In Scala 2, I can provide an implicit operation class wrapping the value in performing intended operation.
// scala 2
trait MyTrait[F[_], A] {
def doSomething[B](f: A => B): F[B]
}
object MyTrait {
implicit class MyTraitOps[T](value: T) extends MyTrait[Option, T] {
override def doSomething[B](f: T => B): Option[B] = Option(value).map(f)
}
}
object App {
def main(args: Array[String]): Unit = {
import MyTrait._
println(3.doSomething(_+1))
}
}
I tried this in Scala 3 with using and given as well as trait argument, but perhaps I am still not familiar with it. The result I’ve attempted looks more complicated and ugly than I expected. For instance, the given instances can’t be imported with something like MyTrait._
and unlike Scala 2, the function doSomething can not be attached to the instance 3
, but the code needs to explicitly supply the value (e.g. using 3). Any suggestions to achieve similar effect - 3.doSomething(_+1)
- in Scala 3 with using and given?
// Scala 3
package c
trait MyTrait[F[_], A](using value: A) {
def doSomething[B](f: A => B): F[B]
}
object MyTrait {
def apply[F[_], T](using instance: MyTrait[F, T]): MyTrait[F, T] = instance
given OptionOps[T](using value: T): MyTrait[Option, T] with
def doSomething[B](f: T => B): Option[B] = Option(value).map(f)
}
@main
def execute(): Unit = {
given instance: MyTrait[Option, Int] = c.MyTrait.OptionOps(using 3)
val f = MyTrait[Option, Int]
println(f.doSomething(_+1))
}
,
Many thanks.
1 Like
Although I can try with following code, this doesn’t exploit given and using. So I appreciate any commentary to achieve the effect like that in Scala 2 if possible.
object App {
trait MyTrait[F[_], A] {
def doSomething[B](f: A => B): F[B]
}
case class OptionOps[A](value: A) extends MyTrait[Option, A] {
def doSomething[B](f: A => B): Option[B] = Option(value).map(f)
}
def main(args: Array[String]): Unit = {
val f = OptionOps(3)
println(f.doSomething(_+1))
}
}
Why not just use extension
?
1 Like
Wait, what?
My naive translation might look like
// scala 3
trait MyTrait[F[_], A]:
def doSomething[B](f: A => B): F[B]
object MyTrait:
extension [A](value: A)
def doSomething[B](f: A => B): Option[B] = Option(value).map(f)
@main def test() = println {
import MyTrait.*
3.doSomething(_+1)
}
but I have not leveraged the trait.
Let me check the docs for writing typeclasses in Scala 3 maybe here
// scala 3
trait MyTypeClass[F[_], A]:
extension [A](value: A) def doSomething[B](f: A => B): Option[B]
object MyTypeClass:
given MyTypeClass[Option, Int] with
extension [A](value: A) def doSomething[B](f: A => B): Option[B] = Option(value).map(f)
@main def test() = println {
import MyTypeClass.given
3.doSomething(_+1)
}
Did it work? I guess so, but I had to change import MyTypeClass.*
to import the given
.
Can I add override
because I’m paranoid? OK, yes I can.
extension [A](value: A) override def doSomething
Can the override go before the extension? No, that is the syntax.
Didn’t I read some discussion about what is the best encoding? I don’t remember if anyone used the old meme where someone is throwing a chair while explaining something. Maybe somebody will point us to the sophisticated talk amongst the smart people.
2 Likes
As @BalmungSan said, you should probably use extension methods here. They cover your use case in a more elegant way than implicit classes and they don’t bring overhead since they desugar into regular methods.
1 Like