【问题标题】:Scala PartialFunction can be Monoid?Scala PartialFunction 可以是 Monoid 吗?
【发布时间】:2012-01-30 17:18:49
【问题描述】:

我认为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")
  }
}

但当前版本的 Scalaz(6.0.4) 不包括在内。有什么不包括在内的原因吗?

【问题讨论】:

  • 我假设你知道Function1 是一个正在合成的幺半群?
  • @dcsobral Function1[A, A],又名Endo[A],是。

标签: scala functional-programming scalaz partialfunction monoids


【解决方案1】:

让我们用不同的眼光来看待这件事。

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

那么,如果我们使用Option[B] 作为我们的Z,我们有什么 Monoid(s)? 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
    }
  }
}

使用这个:

scala> Monoid[Option[Int]].append(Some(1), Some(2))
res9: Option[Int] = Some(3)

但这不是组合两个选项的唯一方法。如果它们都是Some,我们可以简单地选择两者中的第一个或最后一个,而不是附加两个选项的内容。两个触发了这一点,我们使用称为 Tagged Types 的技巧创建了一个不同的类型。这在精神上类似于 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)

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

【讨论】:

  • 非常感谢!我没有意识到 PartialFunction 与 A => LazyOption[B] 同构
  • 谢谢,@retronym!标记类型仅在 scalaz-7 中可用,对于以前的版本,必须使用 FirstOption 特征,对吗?
  • @lester 是的,没错。标记类型有一些锋利的边缘,不幸的是,在我们推荐它们之前,我们可能需要更好的 scalac 支持。示例是:List(Tag(1)) 给出 ClassCastException,因为编译器的一部分将参数视为对象数组,而后面的部分则视为原始数组。
【解决方案2】:

不,这看起来不错,同时满足(非交换)Monoid 的要求。有趣的想法。您要支持什么用例?

【讨论】:

  • @Heiko 抱歉,您的陈述显然是错误的。即使答案是错误的,也远不清楚(至少对我来说)。
【解决方案3】:

你的零肯定违反了恒等元素的公理,但我认为恒等(部分)函数是可以的。

您的追加也不符合 Monoid 定律,但您可以调用 andThen(组合)来代替 orElse。但这仅适用于 A == B:

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

implicit def partialFunctionZero[A]: Zero[A --> A] = new Zero[A --> A] {
  val zero = new (A --> A) {
    def isDefinedAt(a:A) = true
    def apply(a:A) = a
  }
}

【讨论】:

  • 你能举个反例吗?
  • 违反了ea哪个?
  • 你的版本是一个幺半群。 OP 的版本也是一个幺半群。
  • 对我感到羞耻:我认为抛出异常会违反身份,但由于 isDefined 将返回 false,事实并非如此。
  • (S1 orElse zero) == (zero orElse S1) == S1,那么它违反了什么?如果定义了S1,则结果为S1。如果S1zero(即未定义),则结果为zero
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-14
  • 2012-04-02
  • 2016-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多