Scala PartialFunction可以是Monoid吗?

Ken*_*ida 14 functional-programming scala partialfunction scalaz monoids

我认为PartialFunction可以是Monoid.我的思维过程是否正确?例如,

import scalaz._
import scala.{PartialFunction => -->}

implicit def partialFunctionSemigroup[A,B]:Semigroup[A-->B] = new Semigroup[A-->B]{
  def append(s1: A-->B, s2: => A-->B): A-->B = s1.orElse(s2)
}

implicit def partialFunctionZero[A,B]:Zero[A-->B] = new Zero[A-->B]{
  val zero = new (A-->B){
    def isDefinedAt(a:A) = false
    def apply(a:A) = sys.error("error")
  }
}
Run Code Online (Sandbox Code Playgroud)

但目前的版本Scalaz(6.0.4)不包括在内.没有包含某些东西的原因是什么?

ret*_*nym 28

让我们对此有不同的看法.

PartialFunction[A, B]是同构的A => Option[B].(实际上,为了能够在A没有触发评估的情况下检查它是否被定义B,你需要A => LazyOption[B])

所以,如果我们能找到一个Monoid[A => Option[B]]我们已经证明你的断言.

鉴于Monoid[Z],我们可以形成Monoid[A => Z]如下:

implicit def readerMonoid[Z: Monoid] = new Monoid[A => Z] {
   def zero = (a: A) => Monoid[Z].zero
   def append(f1: A => Z, f2: => A => Z) = (a: A) => Monoid[Z].append(f1(a), f2(a))
}
Run Code Online (Sandbox Code Playgroud)

那么,是什么含半幺群(S)我们有,如果我们使用Option[B]作为我们的Z?Scalaz提供三个.主要实例需要一个Semigroup[B].

implicit def optionMonoid[B: Semigroup] = new Monoid[Option[B]] {
  def zero = None
  def append(o1: Option[B], o2: => Option[B]) = o1 match {
    case Some(b1) => o2 match {
       case Some(b2) => Some(Semigroup[B].append(b1, b2)))
       case None => Some(b1)
    case None => o2 match {
       case Some(b2) => Some(b2)
       case None => None
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

使用这个:

scala> Monoid[Option[Int]].append(Some(1), Some(2))
res9: Option[Int] = Some(3)
Run Code Online (Sandbox Code Playgroud)

但这不是组合两个选项的唯一方法.Some我们可以简单地选择两者中的第一个或最后一个,而不是在它们都是两者的情况下附加两个选项的内容.两个触发它,我们创建一个带有标记类型的技巧的独特类型.这与Haskell的精神相似newtype.

scala> import Tags._
import Tags._

scala> Monoid[Option[Int] @@ First].append(Tag(Some(1)), Tag(Some(2)))
res10: scalaz.package.@@[Option[Int],scalaz.Tags.First] = Some(1)

scala> Monoid[Option[Int] @@ Last].append(Tag(Some(1)), Tag(Some(2)))
res11: scalaz.package.@@[Option[Int],scalaz.Tags.Last] = Some(2)
Run Code Online (Sandbox Code Playgroud)

Option[A] @@ First通过它附加Monoid,使用与orElse您的示例相同的语义.

所以,把这一切放在一起:

scala> Monoid[A => Option[B] @@ First]
res12: scalaz.Monoid[A => scalaz.package.@@[Option[B],scalaz.Tags.First]] = 
       scalaz.std.FunctionInstances0$$anon$13@7e71732c
Run Code Online (Sandbox Code Playgroud)

  • 完美的,是的.标记类型有一些锋利的边缘,不幸的是,我们可能需要更好的scalac支持才能推荐它们.例如:`List(Tag(1))`给出一个`ClassCastException`,因为编译器的一部分将参数视为一个对象数组,后一部分作为一个原始数组. (2认同)