0

I have the following trait:

import scalaz.Monoid

trait Mapper[M, R] {
  def map(m: M): R
}

object Mapper {
  @inline implicit def listMapper[M, R]
            (implicit mapper: Mapper[M, R], s: Monoid[R]): Mapper[List[M], R] =
    (xs: List[M]) => xs. foldLeft(s.zero)((r, m) => s.append(r, mapper.map(m)))
}

Now I want to list mapper for with R = String that produces something like the following [mapped_string1, mapped_string2] or $%%""mapped_string1, mapped_string2""%%$.

The question is the following monoid implementation will not work:

implicit val myStringMonoid: Monoid[String] = new Monoid[String] {
  override def zero = ""
  override def append(f1: String, f2: => String) =
    if (f1.isEmpty) f2
    else if(f2.isEmpty) f1
    else f1 + ", " + f2
}

So the following line

println(implicitly[Mapper[List[String], String]].map(List("mapped_string1", "mapped_string2")))

prints mapped_string1, mapped_string2 without angle brackets.

What would be a solution for that case? Maybe just monoids does quite fit my needs. Maybe I need another level of abstraction.

I mean how to generally add some additional operation to be invoked after foldLeft is finished? Without coupling to String or any particular type.

St.Antario
  • 26,175
  • 41
  • 130
  • 318

1 Answers1

2
implicit def listMapper[M, R]
    (implicit mapper: Mapper[M, R], s: Monoid[R]): Mapper[List[M], R] = ???

means that if you have Mapper[M, R] then you have Mapper[List[M], R]. But to make this work you should have some initial Mapper[M, R].

So if you want to have Mapper[List[String], String] you should add for example

implicit def stringMapper: Mapper[String, String] = s => s

Then this produces

println(implicitly[Mapper[List[String], String]].map(List("mapped_string1", "mapped_string2"))) 
//mapped_string1, mapped_string2

def addBrackets(s: String, openBracket: String, closingBracket: String) = 
    openBracket + s + closingBracket

val s = implicitly[Mapper[List[String], String]].map(List("mapped_string1", "mapped_string2"))
    println(addBrackets(s, "[", "]"))
//[mapped_string1, mapped_string2]

Otherwise you can change

implicit val myStringMonoid: Monoid[String] = new Monoid[String] {
    override def zero = ""
    override def append(f1: String, f2: => String): String =
      if (f1.isEmpty) f2
      else if(f2.isEmpty) f1
      else f1 + f2 // without  ", "
  }

Then

val l = "[" ::
  List("mapped_string1", "mapped_string2")
    .flatMap(s => List(", ", s))
    .tail ::: List("]")
println(implicitly[Mapper[List[String], String]].map(l))
//[mapped_string1, mapped_string2]
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Yes this is a solution, but is it possible to abstract over types of Monoid (`String` in my case). For instance, I want to get a mapper for `Array[Byte]` and in the case I don't need any surroundings (like `[` and `]` in the case of strings). But such `implicit def listMapper[M, R]` implemetation does not work in that case. Or I missed something? – St.Antario Nov 20 '17 at 11:16
  • I guess you should define `implicit def byteMapper: Mapper[Byte, Byte] = b => b` and `implicit def arrayMapper[M, R](implicit mapper: Mapper[M, R], s: Monoid[R]): Mapper[Array[M], R] = ???` – Dmytro Mitin Nov 20 '17 at 14:06