I have an implicit helper set up like this:
trait Helper[T] {
def help(entry: T): Unit
}
object Helpers {
implicit object XHelper extends Helper[X] {
override def help(entry: X): Unit = {println("x")}
}
implicit object YHelper extends Helper[Y] {
override def help(entry: Y): Unit = {println("y")}
}
def help[T](entry: T)(implicit helper: Helper[T]): Unit = {
helper.help(entry)
}
}
I would like to set up a collection of elements and run help on each of them. However, the following gives a compiler error because we can't guarantee all elements have matching Helpers:
val data = Seq[_](new X(), new Y())
data.foreach(entry => Helpers.help(entry))
If we had a generic type T we could enforce the implicit constraint on it with [T: Helper], but that doesn't work on _. How can I enforce that each element of data has a matching Helper?
In Scala context bound like class A[T: Typeclass] is just syntactic sugar for class A[T](implicit ev: Typeclass[T]). Unlike T <: Base or T >: Super, context bound is not really a part of a type signature, so you can't have a signature like val b: Box[T: Typeclass].
If you want to run typeclass operations on elements of some container, you'd have to pack relevant typeclass instances together with the values in the container.
A possible implementation of this may look as follows:
import language.higherKinds
import language.implicitConversions
// Class that packs values with typeclass instances
class WithTC[T, TC[_]](t: T)(implicit tc: TC[T]) {
// Some helper methods to simplify executing typeclass operations
// You may just make `t` and `tc` public, if you wish.
def apply[U](op: (TC[T], T) => U) = op(tc, t)
def apply[U](op: T => TC[T] => U) = op(t)(tc)
}
object WithTC {
// Implicit conversion to automatically wrap values into `WithTC`
implicit def apply[T, TC[_]](t: T)(implicit tc: TC[T]): WithTC[T, TC] =
new WithTC(t)(tc)
}
Then you can make a sequence with existentially typed elements:
import Helpers._
val data: Seq[(T WithTC Helper) forSome { type T }] = Seq(new X(), new Y())
And execute typeclass operations on the sequence elements:
// The following lines produce equivalent results
data.foreach(_(_ help _))
data.foreach(_(t => implicit tc => Helpers.help(t)))
data.foreach(_(t => Helpers.help(t)(_)))
It's not possible with type like Seq since it is only parametrized for one element type that is common for all its elements.
However, you can achieve this with Shapeless HLists and polymorphics functions (Poly):
class X
class Y
trait Helper[T] {
def help(entry: T): Unit
}
object Helpers {
implicit object XHelper extends Helper[X] {
override def help(entry: X): Unit = println("x")
}
implicit object YHelper extends Helper[Y] {
override def help(entry: Y): Unit = println("y")
}
}
import shapeless._
object helper extends Poly1 {
implicit def tCase[T: Helper]: Case.Aux[T, Unit] =
at(implicitly[Helper[T]].help(_))
}
val hlist = new X :: new Y :: HNil
hlist.map(helper)
// Output:
x
y
Related
I am struggling a little with scala 2.12:
I have the following hierarchy:
trait A
case class B(format: String) extends A
trait Writer {
def write(config: A): Unit
}
val writer = new Writer {
override def write(config: A) = println("hi")
}
val w = B("console")
writer.write(w)
which works fine. But I want to provide an alternate implementation for writer:
val writer = new Writer {
override def write(config: B) = println("hi")
}
But I get object creation impossible, since method write in trait Writer of type (config: Playground.A)Unit is not defined
I assumed that since B is an A, this should work. How can I override write with a config of type B where B <: A
Scastie: https://scastie.scala-lang.org/QBaiiDP4Sj2lptUjrWLJYw
EDIT: ------------------------------------------------------------
Based on some inputs, I changed the implementation to:
sealed trait A
case class B(format: String) extends A
trait Writer[+T] {
def write[S >: T](config: S): Unit
}
val writer: Writer[A] = new Writer[B] {
override def write[B](config: B) = println("hi")
}
val b = B("console")
writer.write(b)
which works.
But if I modify it to access the variables in config, it breaks:
sealed trait A
case class B(format: String) extends A
trait Writer[+T] {
def write[S >: T](config: S): Unit
}
val writer: Writer[A] = new Writer[B] {
override def write[B](config: B) = println(config.format)
}
val b = B("console")
writer.write(b)
with value format is not a member of type parameter B
https://scastie.scala-lang.org/Xj2rKbbiTmG7raZgQZYfHA
Appreciate the inputs.
You're very close with your latest version. As Matthias Berndt pointed out, the write method declares a new type parameter, but should use the one declared on the trait. In addition, the type parameter should be contravariant.
This code compiles and prints console:
sealed trait A
case class B(format: String) extends A
trait Writer[-T <: A] {
def write(config: T): Unit
}
val writer: Writer[B] = new Writer[B] {
override def write(config: B) = println(config.format)
}
val b = B("console")
writer.write(b)
Note that, because B is a subtype of A, you can also use a Writer[A] with an instance of B. Because Writer is contravariant, you can assign a value of type Writer[A] to a variable of type Writer[B]:
val aWriter: Writer[B] = new Writer[A] {
override def write(config: A) = println(s"Got A: $config")
}
aWriter.write(b) // prints "Got A: B(console)"
You can't do the opposite (assign a Writer[B] value to a Writer[A] variable) because a Writer[A] would be able to accept any value of type A, while a Writer[B] can only accept values of type B.
https://scastie.scala-lang.org/TimMoore/bd5E1p99TLCDVfMbElKqFg/8
It doesn't work because Writer declares that its write method will accept an arbitrary A. What if someone decides to pass an A that is not a B to writer.write? Then it wouldn't work, so the compiler stops you from doing that.
I have written this code
trait Input[F[_]] {
def read: F[String]
def write(str: String) : F[Unit]
def getName : F[String] = for {
_ <- write("What is your name")
name <- read
} yield name
}
This code doesn't compile obviously because the compiler has no way of knowing that the type F supports flatmap. So I change my code to
import scalaz._
import Scalaz._
trait Input[F[_] : Monad] {
def read: F[String]
def write(str: String) : F[Unit]
def getName : F[String] = for {
_ <- write("What is your name")
name <- read
} yield name
}
but now I get compile time error traits cannot have type parameters with context bounds.
So how can I specify constraints on my type parameter so that it always supports flatmap?
trait Input[F[_]: Monad] would create an implicit constructor parameter and traits can't have constructor parameters (until Scala 3). def testFunc[F[_]: Monad] would create an implicit parameter. For instance:
def testFunc[F[_]: Monad](arg: Int) = ???
class TestClass[F[_]: Monad] {}
Will work because it is translated to:
def testFunc[F[_]](arg: Int)(implicit ev: Monad[F]) = ???
class TestClass[F[_]](implicit val ev: Monad[F]) {}
I.e. [F[_]: Monad] is syntactic sugar for [F[_]] with implicit val ev: Monad[F]. And traits has no constructor to pass parameters until Scala 3.
For your case, if you really need to inside a trait to constraint F to a Monad for example, then:
trait Input[F[_]] {
val M: Monad[F]
def read: F[String]
def write(str: String) : F[Unit]
def getName : F[String] = M.flatMap(write("What is your name"))(_ => read)
}
I.e. you are saying to one who implements, that "you can implement Input, as soon as you have Monad[F]". Then you can use it like:
object Main extends App{
class IOInput extends Input[IO] {
override val M: Monad[IO] = Monad[IO]
override def read: IO[String] = IO("red")
override def write(str: String): IO[Unit] = IO(println(s"write: $str"))
}
val impl = new IOInput
println(impl.getName.unsafeRunSync())
}
P.S. But for me, it seems like something is wrong. You are kinda defining effects in trait Input and using them right in the same trait. Reads weird for me at least. Probably getName should be somewhere else.
Let's say we have the following traits:
trait MyValue
object MyValue {
case class MyBoolean(record: Boolean) extends MyValue
case class MyLong(record: Long) extends MyValue
}
trait MyValueExtractor[T] {
def apply(record: T): Option[MyValue]
}
trait MyThing[T] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
}
What I want is something like this but without the second type parameter.
Note: I can't actually update the MyThing trait; I'm just using this as an illustration of the intended functionality.
trait MyThing[T, U] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
def myRelatedValue(record: T): Option[U]
}
I'm wondering if I could use the type class pattern to help solve this (i.e., import some rich class that implicitly gives me a myRelatedValue method)?
Here's the rub. Every time T (above) is MyValue.MyBoolean, U must be a String. Every time T is MyValue.MyLong, U must be a Double. In other words, there's a sort of underlying mapping between T and U.
Is there a good way to do this using type class?
Sure. You just need to define some Mapping typeclass with implementations for your desired pairs of types. Then MyThing can have a method that takes an implicit typeclass instance and simply invokes its method.
Here's the code (I removed the unneeded details)
// types
case class MyBoolean(record: Boolean)
case class MyLong(record: Long)
// trait which uses the Mapping typeclass
trait MyThing[T] {
def myRelatedValue[U](record: T)(implicit ev: Mapping[T, U]): Option[U] = ev.relatedValue(record)
}
// typeclass itself
trait Mapping[T, U] {
def relatedValue(record: T): Option[U]
}
object Mapping {
implicit val boolStringMapping = new Mapping[MyBoolean, String] {
def relatedValue(record: MyBoolean) = Some(record.record.toString)
}
implicit val longDoubleMapping = new Mapping[MyLong, Double] {
def relatedValue(record: MyLong) = Some(record.record)
}
}
// usage
val myBoolThing = new MyThing[MyBoolean] {}
val myLongThing = new MyThing[MyLong] {}
val myStringThing = new MyThing[String] {}
myBoolThing.myRelatedValue(MyBoolean(true)) // Some(true)
myLongThing.myRelatedValue(MyLong(42L)) // Some(42.0)
myStringThing.myRelatedValue("someString") // error: could not find implicit value
Note that e.g. myBoolThing.myRelatedValue(MyBoolean(true)) will yield a type Option[U]. However, since myRelatedValue is parameterized, you can help the compiler and invoke it as myBoolThing.myRelatedValue[String](MyBoolean(true)), in which case you will obtain an Option[String]. If you try something other than String for MyBoolean, you will get an error.
Using context bounds in scala you can do stuff like
trait HasBuild[T] {
def build(buildable: T): Something
}
object Builders {
implict object IntBuilder extends HasBuild[Int] {
override def build(i: Int) = ??? // Construct a Something however appropriate
}
}
import Builders._
def foo[T: HasBuild](input: T): Something = implicitly[HasBuild[T]].build(1)
val somethingFormInt = foo(1)
Or simply
val somethingFromInt = implicitly[HasBuild[Int]].build(1)
How could I express the type of a Seq of any elements that have an appropriate implicit HasBuild object in scope? Is this possible without too much magic and external libraries?
Seq[WhatTypeGoesHere] - I should be able to find the appropriate HasBuild for each element
This obviously doesn't compile:
val buildables: Seq[_: HasBuild] = ???
Basically I'd like to be able to handle unrelated types in a common way (e.g.: build), without the user wrapping them in some kind of adapter manually - and enforce by the compiler, that the types actually can be handled. Not sure if the purpose is clear.
Something you can do:
case class HasHasBuild[A](value: A)(implicit val ev: HasBuild[A])
object HasHasBuild {
implicit def removeEvidence[A](x: HasHasBuild[A]): A = x.value
implicit def addEvidence[A: HasBuild](x: A): HasHasBuild[A] = HasHasBuild(x)
}
and now (assuming you add a HasBuild[String] for demonstration):
val buildables: Seq[HasHasBuild[_]] = Seq(1, "a")
compiles, but
val buildables1: Seq[HasHasBuild[_]] = Seq(1, "a", 1.0)
doesn't. You can use methods with implicit HasBuild parameters when you have only a HasHasBuild:
def foo1[A](x: HasHasBuild[A]) = {
import x.ev // now you have an implicit HasBuild[A] in scope
foo(x.value)
}
val somethings: Seq[Something] = buildables.map(foo1(_))
First things first, contrary to some of the comments, you are relying on context bounds. Requesting an implicit type class instance for a T is what you call a "context bound".
What you want is achievable, but not trivial and certainly not without other libraries.
import shapeless.ops.hlist.ToList
import shapeless._
import shapeless.poly_
object builder extends Poly1 {
implicit def caseGeneric[T : HasBuilder] = {
at[T](obj => implicitly[HasBuilder[T]].build(obj))
}
}
class Builder[L <: HList](mappings: L) {
def build[HL <: HList]()(
implicit fn: Mapper.Aux[builder.type, L, HL],
lister: ToList[Something]
) = lister(mappings map fn)
def and[T : HasBuilder](el: T) = new Builder[T :: L](el :: mappings)
}
object Builder {
def apply[T : HasBuilder](el: T) = new Builder(el :: HNil)
}
Now you might be able to do stuff like:
Builder(5).and("string").build()
This will call out the build methods from all the individual implcit type class instances and give you a list of the results, where every result has type Something. It relies on the fact that all the build methods have a lower upper bound of Something, e.g as per your example:
trait HasBuild[T] {
def build(buildable: T): Something
}
I'm very new to Scala programming, and I really like the degree to which code is composable. I wanted to write some traits that deal with two related objects that are convertible to each other, and build more functionality by continuing to extend that trait so that when I create objects I can specify the related types for my generics. Here is a working toy example of the type of code I'm talking about:
trait FirstConverter[First] {
def toFirst: First
}
trait SecondConverter[Second] {
def toSecond: Second
}
trait TwoWayConverter[First <: SecondConverter[Second], Second <: FirstConverter[First]] {
def firstToSecond(x: First) = x.toSecond
def secondToFirst(x: Second) = x.toFirst
}
trait RoundTripConverter[First <: SecondConverter[Second], Second <: FirstConverter[First]] extends TwoWayConverter[First, Second] {
def firstToFirst(x: First) = secondToFirst(firstToSecond(x))
def secondToSecond(x: Second) = firstToSecond(secondToFirst(x))
}
case class A(s: String) extends SecondConverter[B] {
def toSecond: B = B((s.toInt) + 1)
}
case class B(i: Int) extends FirstConverter[A] {
def toFirst: A = A((i * 2).toString)
}
object ABConverter extends RoundTripConverter[A, B]
object Main {
def main(args: Array[String]): Unit = {
println(ABConverter firstToSecond A("10")) // 11
println(ABConverter secondToFirst B(42)) // 84
println(ABConverter firstToFirst A("1")) // 4
println(ABConverter secondToSecond B(2)) // 5
}
}
While this works, I'm not sure if it's idiomatic Scala. I'm asking if there are any tricks to make the type definitions more concise and if I can somehow define the type restrictions only once and have them used by multiple traits which extend other traits.
Thanks in advance!
One way to improve your design would be to use a type class instead of inheriting from FirstConverter and SecondConverter. That way you could use multiple conversion functions for the same types and convert between classes you don't control yourself.
One way would be to create a type class which can convert an A into a B :
trait Converter[A, B] {
def convert(a: A): B
}
trait TwoWayConverter[A, B] {
def firstToSecond(a: A)(implicit conv: Converter[A, B]): B = conv.convert(a)
def secondToFirst(b: B)(implicit conv: Converter[B, A]): A = conv.convert(b)
}
trait RoundTripConverter[A, B] extends TwoWayConverter[A, B] {
def firstToFirst(a: A)(implicit convAB: Converter[A, B], convBA: Converter[B, A]) =
secondToFirst(firstToSecond(a))
def secondToSecond(b: B)(implicit convAB: Converter[A, B], convBA: Converter[B, A]) =
firstToSecond(secondToFirst(b))
}
We could create type class instances for the following two classes Foo and Bar similar to your A and B
case class Foo(s: String)
case class Bar(i: Int)
implicit val convFooBarFoor = new Converter[Foo, Bar] {
def convert(foo: Foo) = Bar((foo.s toInt) + 1)
}
implicit val convBarFoo = new Converter[Bar, Foo] {
def convert(bar: Bar) = Foo((bar.i * 2) toString)
}
We then could create a FooBarConverter :
object FooBarConverter extends RoundTripConverter[Foo, Bar]
FooBarConverter firstToSecond Foo("10") // Bar(11)
FooBarConverter secondToFirst Bar(42) // Foo(84)
FooBarConverter firstToFirst Foo("1") // Foo(4)
FooBarConverter secondToSecond Bar(2) // Bar(5)
The only problem is because we can not pass parameters to a trait, we can not limit the types to types with a Converter type class instance. So you can create the StringIntConverter below even if no Converter[String, Int] and/or Convert[Int, String] instances exist.
object StringIntConverter extends TwoWayConverter[String, Int]
You cannot call StringIntConverter.firstToSecond("a") because the firstToSecond method needs the implicit evidence of the two mentioned type class instances.