I'm reading Functional Programming in Scala by Chiusano and Bjarnason.
In Chapter 10, they define the Monoid trait:
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
Two example instances of the trait are the following:
1) String Monoid:
val stringMonoid = new Monoid[String] {
def op(a1: String, a2: String) = a1 + a2
val zero = ""
}
2) List Monoid:
def listMonoid[A] = new Monoid[List[A]] {
def op(a1: List[A], a2: List[A]) = a1 ++ a2
val zero = Nil
}
My question is, why do we use val in the case of stringMonoid, and def in listMonoid?
stringMonoid is a string type new Monoid[String]
listMonoid is a generic type new Monoid[List[A]]. To pass this generic type A it is declared as def
def listMonoid[A] = new Monoid[List[A]]
Edit
To address the comment:
If you still want is to use val for listMonoid, then define what list is going to have in it.
val listMonoid = new Monoid[List[Int]] {
def op(a1: List[Int], a2: List[Int]) = a1 ++ a2
def zero = List.empty
}
Related
When I have a function in Scala:
def toString[T: Show](xs: T*): String = paths.map(_.show).mkString
And the following type class instances in scope:
implicit val showA: Show[MyTypeA]
implicit val showB: Show[MyTypeB]
I can use function toString in the following ways:
val a1: MyTypeA
val a2: MyTypeA
val stringA = toString(a1, a2)
val b1: MyTypeB
val b2: MyTypeB
val stringB = toString(b1, b2)
But I cannot call toString mixing parameters of type MyTypeA and MyTypeB:
// doesn't compile, T is inferred to be of type Any
toString(a1, b1)
Is it possible to redefine toString in such a way that it becomes possible to mix parameters of different types (but only for which a Show typeclass is available)?
Note that I am aware of the cats show interpolator which solves this specific example, but I'm looking for a solution which can be applied to different cases as well (e.g. toNumber).
I am also aware of circumventing the problem by calling .show on the parameters before passing them to the toString function, but I'm looking for a way to avoid this as it results in code duplication.
Example with shapeless:
object myToString extends ProductArgs { //ProductArgs allows changing variable number of arguments to HList
//polymorphic function to iterate over values of HList and change to a string using Show instances
object showMapper extends Poly1 {
implicit def caseShow[V](implicit show: Show[V]): Case.Aux[V, String] = {
at[V](v => show.show(v))
}
}
def applyProduct[ARepr <: HList](
l: ARepr
)(
implicit mapper: Mapper[showMapper.type, ARepr]
): String = l.map(showMapper).mkString("", "", "")
}
Now let's test it:
case class Test1(value: String)
case class Test2(value: String)
case class Test3(value: String)
implicit val show1: Show[Test1] = Show.show(_.value)
implicit val show2: Show[Test2] = Show.show(_.value)
println(myToString(Test1("a"), Test2("b"))) //"ab"
println(myToString(Test1("a"), Test2("b"), Test3("c"))) //won't compile since there's no instance of Show for Test3
By the way, I think toString is not the best name, because probably it can cause weird conflicts with toString from java.lang.Object.
If you don't want to mess with shapeless, another solution that comes to my mind is to just create functions with different arity:
def toString[A: Show](a: A): String = ???
def toString[A: Show, B: Show](a: A, b: B): String = ???
//etc
It's definitely cumbersome, but it might be the easiest way to solve your problem.
Here's one way to do it in Dotty (note that most of the Dotty-specific features used here are not necessary; they're just to make life easier, but being able to abstract over tuples of different arities is something you can't do (easily) in Scala 2):
opaque type Show[T] = T => String
opaque type ShowTuple[T <: Tuple] = T => String
object ShowTuple {
given ShowTuple[EmptyTuple] = _ => ""
given showTuple[H, T <: Tuple](using show: Show[H], showTail: ShowTuple[T]) as ShowTuple[H *: T] =
{ case h *: t => show(h) + "," + showTail(t) }
}
def multiToString[T <: Tuple](t: T)(using showTuple: ShowTuple[T]) =
showTuple(t)
It can be used like this:
class TypeA(val i: Int)
class TypeB(val s: String)
class TypeC(val b: Boolean)
given Show[TypeA] = t => s"TypeA(${t.i})"
given Show[TypeB] = t => s"TypeB(${t.s})"
given Show[TypeC] = t => s"TypeC(${t.b})"
println(multiToString((new TypeA(10), new TypeB("foo"), new TypeC(true))))
Using a type for which an implicit is not given fails:
class TypeD
multiToString((new TypeA(10), new TypeB("foo"), new TypeC(true), new TypeD))
Try it in Scastie
What is the type of paths?
If it's List[T] then there should be an implicit Show[T] in scope.
If it's List[Any] then there should be an implicit Show[Any] in scope.
If paths contains elements of different types and paths is not a List[Any] then paths shouldn't be a List[...] at all. It can be of type L <: HList. You can try
import shapeless.{HList, HNil, Poly1, Poly2}
import shapeless.ops.hlist.{LeftReducer, Mapper}
trait Show[T] {
def show(t: T): String
}
implicit class ShowOps[T](t: T) {
def show(implicit s: Show[T]): String = s.show(t)
}
object show extends Poly1 {
implicit def cse[T: Show]: Case.Aux[T, String] = at(_.show)
}
object concat extends Poly2 {
implicit def cse: Case.Aux[String, String, String] = at(_ + _)
}
def toString[L <: HList, L1 <: HList](xs: L)(implicit
mapper: Mapper.Aux[show.type, L, L1],
reducer: LeftReducer.Aux[L1, concat.type, String]
): String = xs.map(show).reduceLeft(concat)
type MyTypeA
type MyTypeB
implicit val showA: Show[MyTypeA] = ???
implicit val showB: Show[MyTypeB] = ???
val a1: MyTypeA = ???
val b1: MyTypeB = ???
toString(a1 :: b1 :: HNil)
Suppose I have some function that should take a sequence of Ints or a sequence of Strings.
My attempt:
object Example extends App {
import scala.util.Random
val rand: Random.type = scala.util.Random
// raw data
val x = Seq(1, 2, 3, 4, 5).map(e => e + rand.nextDouble())
val y = Seq("chc", "asas")
def f1[T <: AnyVal](seq: Seq[T]) = {
println(seq(0))
}
// this works fine as expected
f1(x)
// how can i combine
f1(y)
}
How can I add this to also work with strings?
If I change the method signature to:
def f1[T <: AnyVal:String](seq: Seq[T])
But this won't work.
Is there a way to impose my required constraint on the types elegantly?
Note the difference between an upper bound
A <: C
and a context bound
A : C
so type parameter clause [T <: AnyVal : String] does not make much sense. Also types such as String are rarely (or never) used as context bounds.
Here is a typeclass approach
trait EitherStringOrAnyVal[T]
object EitherStringOrAnyVal {
implicit val str: EitherStringOrAnyVal[String] = new EitherStringOrAnyVal[String] {}
implicit def aval[T <: AnyVal]: EitherStringOrAnyVal[T] = new EitherStringOrAnyVal[T] {}
}
def f1[T: EitherStringOrAnyVal](seq: Seq[T]): Unit = {
println(seq(0))
}
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok
or generelized type constraints approach
object Foo {
private def impl[T](seq: Seq[T]): Unit = {
println(seq(0))
}
def f1[T](seq: Seq[T])(implicit ev: T =:= String): Unit = impl(seq)
def f1[T <: AnyVal](seq: Seq[T]): Unit = impl(seq)
}
import Foo._
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok
I feel like you should write a separate function for both, but there are other ways to do it:
In Dotty, you can just use union types, which is what I'd recommend here:
Edit: As per Alexey Romanov’s suggestion, replaced Seq[AnyVal | String] with Seq[AnyVal | String], which was wrong,
def foo(seq: Seq[AnyVal] | Seq[String]): Unit = {
println(seq)
}
Scastie
If you're not using Dotty, you can still do this in Scala 2 with a couple reusable implicit defs
class Or[A, B]
implicit def orA[A, B](implicit ev: A): Or[A, B] = new Or
implicit def orB[A, B](implicit ev: B): Or[A, B] = new Or
def foo[T](seq: Seq[T])(implicit ev: Or[T <:< AnyVal, T =:= String]): Unit =
println(seq)
foo(Seq("baz", "waldo"))
foo(Seq(23, 34))
foo(Seq(List(), List())) //This last one fails
Scastie
You can also do it with a typeclass, as Luis Miguel Mejía Suárez suggested, but I wouldn't recommend it for such a trivial task since you still have to define 2 functions for each type, along with a trait, 2 implicit objects, and one function that can use instances of that trait. You probably don't want this pattern to handle just 2 different types.
sealed trait DoSomethingToIntOrString[T] {
def doSomething(t: Seq[T]): Unit
}
implicit object DoSomethingToAnyVal extends DoSomethingToAnyValOrString[AnyVal] {
def doSomething(is: Seq[AnyVal]): Unit = println(is)
}
implicit object DoSomethingToString extends DoSomethingToIntOrString[String] {
def doSomething(ss: Seq[String]): Unit = println(ss)
}
def foo[T](t: Seq[T])(implicit dsis: DoSomethingToIntOrString[T]): Unit = {
dsis.doSomething(t)
}
foo(Seq("foo", "bar"))
foo(Seq(1, 2))
In Scastie
def f1( seq: Seq[Any] ): Unit =
println( seq( 0 ) )
This works, because the least common supertype of Int and String (the 'lowest' type that is a supertype of both) would be Any, since Int is a subtype of AnyVal (i.e. 'the primitives) and String is a subtype of AnyRef (i.e. `the heap objects). f1 does not depends in any way on the contents of the list and is not polymorphic in its return type (Unit), so we don't need a type parameter at all.
I'm trying to express the following idea:
Function caseClassFields should return an array of (String, T) pairs, by processing a case class.
I put upper bound for T, expecting that it should be a subtype of AnyRef or AnyRef itself.
Here is a function:
def caseClassFields[T <: AnyRef](obj: AnyRef): Array[(String, T)] = {
val metaClass = obj.getClass
metaClass.getDeclaredFields.map {
field => {
field.setAccessible(true)
(field.getName, field.get(obj))
}
}
}
But unfortunately I get following error:
Expression of type Array[(String, AnyRef)] doesn't conform to expected type Array[(String, T)]
How to fix this?
Doing what you want with reflection and keeping type safety are orthogonal requirements. But shapeless, a library for generic derivation, can do what you want and still keep you type safe.
Here's a short example using shapeless to get you started.
We first define our algebra:
sealed trait ValidatableField
case class ValidatableString(value: Boolean)
extends ValidatableField
case class ValidatableInt(value: Boolean) extends ValidatableField
case class ValidatableRecord(fields: List[(String, ValidatableField)])
extends ValidatableField
Now we define our validator trait:
trait Validator[T] {
def validate(value: T): ValidatableField
}
trait RecordValidator[T] extends Validator[T] {
def validate(value: T): ValidatableRecord
}
Now lets define, for the sake of the example, validation on Int and String:
implicit val intValidator = new Validator[Int] {
override def validate(t: Int): ValidatableField = ValidatableInt(t > 42)
}
implicit val stringValidator = new Validator[String] {
override def validate(t: String): ValidatableField = ValidatableString(t.length < 42)
}
Now we define a generic implementation for HList which will cover our ValidatableRecord which is the generic representation of our case class:
implicit val hnilEncoder: RecordValidator[HNil] = new RecordValidator[HNil] {
override def validate(value: HNil): ValidatableRecord = ValidatableRecord(Nil)
}
implicit def hlistValidator[K <: Symbol, H, T <: HList](
implicit witness: Witness.Aux[K],
hEncoder: Lazy[Validator[H]],
tEncoder: RecordValidator[T]
): RecordValidator[FieldType[K, H] :: T] = {
val fieldName = witness.value.name
new RecordValidator[::[FieldType[K, H], T]] {
override def validate(value: ::[FieldType[K, H], T]): ValidatableRecord = {
val head = hEncoder.value.validate(value.head)
val tail = tEncoder.validate(value.tail)
ValidatableRecord((fieldName, head) :: tail.fields)
}
}
}
implicit def genericEncoder[A, H <: HList](
implicit generic: LabelledGeneric.Aux[A, H],
hEncoder: Lazy[RecordValidator[H]]): Validator[A] = {
new RecordValidator[A] {
override def validate(value: A): ValidatableRecord =
hEncoder.value.validate(generic.to(value))
}
}
With this much code, we can now validate any case class which has a String and Int field in it, and it is trivial to add other validator for more primitives:
object Test {
def main(args: Array[String]): Unit = {
case class Foo(s: String, i: Int)
val foo = Foo("hello!", 42)
println(Validator[Foo].validate(foo))
}
}
Yields:
ValidatableRecord(List((s,ValidatableString(true)), (i,ValidatableInt(false))))
I know this can be overwhelming a bit, but David Gurnells "Guide To Shapeless" is a great place to get started.
The reason is field.get(obj) returns AnyRef while your return type is T. Therefore, you need to convert it into T. However, I don't see any use of Generic type T in your code, so you can simply change the return type to Array[(String, AnyRef)].
def caseClassFields[T <: AnyRef](obj: AnyRef): Array[(String, AnyRef)]
However, if you insist to use Generic, you need to convert field.get(obj) to type T. Note, that you might get exception in case of invalid type while converting to type T.
def caseClassFields[T <: AnyRef](obj: AnyRef): Array[(String, T)] = {
val metaClass = obj.getClass
metaClass.getDeclaredFields.map {
field => {
field.setAccessible(true)
(field.getName, field.get(obj).asInstanceOf[T])
}
}
}
case class Foo(name:String)
val result:Array[(String, String)] = caseClassFields[String](Foo("bar"))
As discussed in the comments you probably want to use shapeless, but to elaborate.
The method field.get() isnt related to the type parameter. Normally you would use a type parameter like this
def caseClassFields[T <: AnyRef](obj: T): Array[(String, T)] = ???
or this ..
def caseClassFields[T <: AnyRef](obj: Container[T]): Array[(String, T)] = ???
If there was some link between field.get and T it could work, but the relationship would need to be proven to the compiler. As the T can be anything the compiler cant prove anything about it.
I can strongly recommend this book the 'Type Astronaut’s Guide to Shapeless' as a intro to the topic.
http://underscore.io/books/shapeless-guide/
https://github.com/milessabin/shapeless
Let's say that I have a Monoid trait as below:
trait Monoid[A] {
def combine(a1: A, a2: A): A
def identity: A
}
Now if I want to write an optionMonoid for this, I could write it like this:
val optionMonoid1 = new Monoid[Option[A]] {
def combine(a1: Option[A], a2: Option[A2]) a1 orElse a2
def identity = None
}
This given the fact that I do not know anything about the inner type in the Option. But what if I want to have the combine operator in such a way that I want to really combine the inner types in the Option?
One option:
trait Semigroup[A] {
def combine(a1: A, a2: A): A
}
trait Monoid[A] extends Semigroup[A] {
def identity: A
}
def optionMonoid2[A](implicit sgA: Semigroup[A]) = new Monoid[Option[A]] {
def combine(a1: Option[A], a2: Option[A2]) = (a1, a2) match {
case (Some(b1), Some(b2)) => Some(sgA.combine(b1, b2))
case _ => a1.orElse(a2)
}
def identity = None
}
It's easy to verify monoid laws hold.
Going through the first exercise of FP in Scala's Monoid chapter, a compile-time error occurred when trying to create a new implementation of a trait in with "listMonoid."
object MonoidTesting1 {
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
val listMonoid = new Monoid[List[A]] {
def op(a1: List[A], a2: List[A]) = a1 ++ a2
val zero = Nil
}
}
C:\Users\Kevin\Workspace\side-work\Monoid>scalac MonoidTesting.scala
MonoidTesting.scala:12: error: not found: type A
val listMonoid = new Monoid[List[A]] {
^
MonoidTesting.scala:13: error: not found: type A
def op(a1: List[A], a2: List[A]) = a1 ++ a2
^
MonoidTesting.scala:13: error: not found: type A
def op(a1: List[A], a2: List[A]) = a1 ++ a2
^
three errors found
How can I create listMonoid in order to use an unspecified, generic type?
List is what actually called a free monoid. You need explicitly create A in scope of list Monoid:
object MonoidTest {
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
def listMonoid[A] = new Monoid[List[A]] {
def op(a1: List[A], a2: List[A]) = a1 ++ a2
val zero = List.empty[A]
}
}