【问题标题】:Simple composition of implicits with Monoids in Scala在 Scala 中使用 Monoids 进行隐式的简单组合
【发布时间】:2017-11-19 17:57:56
【问题描述】:

我有以下特点:

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)))
}

现在我想用R = String 列出映射器,它会产生类似下面的[mapped_string1, mapped_string2]$%%""mapped_string1, mapped_string2""%%$.

问题是以下 monoid 实现将不起作用:

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
}

所以下面一行

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

打印mapped_string1, mapped_string2 不带尖括号

这种情况有什么解决方案?也许只有幺半群确实很适合我的需要。也许我需要另一个抽象层次。

我的意思是如何在foldLeft 完成后添加一些要调用的附加操作?不耦合到 String 或任何特定类型。

【问题讨论】:

    标签: scala monoids


    【解决方案1】:
    implicit def listMapper[M, R]
        (implicit mapper: Mapper[M, R], s: Monoid[R]): Mapper[List[M], R] = ???
    

    表示如果你有Mapper[M, R],那么你就有Mapper[List[M], R]。但要完成这项工作,您应该有一些初始的Mapper[M, R]

    所以如果你想拥有Mapper[List[String], String],你应该添加例如

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

    那么这就产生了

    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]
    

    否则你可以改变

    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  ", "
      }
    

    然后

    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]
    

    【讨论】:

    • 是的,这是一个解决方案,但是否可以抽象出 Monoid 的类型(在我的情况下为 String)。例如,我想为Array[Byte] 获取一个映射器,并且在我不需要任何环境的情况下(例如[] 在字符串的情况下)。但是这种implicit def listMapper[M, R] 实现在这种情况下不起作用。还是我错过了什么?
    • 我猜你应该定义implicit def byteMapper: Mapper[Byte, Byte] = b => bimplicit def arrayMapper[M, R](implicit mapper: Mapper[M, R], s: Monoid[R]): Mapper[Array[M], R] = ???
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多