foldLeft, foldRight, other Foldable specializations (#11592)

* FoldableContravariant, a mapping for Foldable instances

* use FoldableContravariant to specialize several ImmArraySeq, NonEmpty methods

* folding specializations for ImmArray

* a few docs for FoldableContravariant

* specializations for FrontStack

* no changelog

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Stephen Compall 2021-11-09 17:24:26 -05:00 committed by GitHub
parent 92dfcdeb24
commit b87acab897
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 15 deletions

View File

@ -4,8 +4,6 @@
package com.daml.lf.data
import ScalazEqual.{orderBy, toIterableForScalazInstances}
import scalaz.syntax.applicative._
import scalaz.syntax.traverse._
import scalaz.{Applicative, Order, Traverse}
import scala.annotation.tailrec
@ -154,13 +152,28 @@ object FrontStack extends FrontStackInstances {
implicit val `FrontStack covariant`: Traverse[FrontStack] = new Traverse[FrontStack] {
override def traverseImpl[G[_]: Applicative, A, B](
fa: FrontStack[A]
)(f: A => G[B]): G[FrontStack[B]] =
)(f: A => G[B]): G[FrontStack[B]] = {
import scalaz.syntax.applicative._, scalaz.syntax.traverse._
fa.toBackStack.bqFoldRight(FrontStack.empty[B].pure[G])(
(a, z) => ^(f(a), z)(_ +: _),
(iaa, z) => ^(iaa traverse f, z)(_ ++: _),
)
}
override def map[A, B](fa: FrontStack[A])(f: A => B) = fa map f
override def foldLeft[A, B](fa: FrontStack[A], z: B)(f: (B, A) => B) =
fa.iterator.foldLeft(z)(f)
override def foldRight[A, B](fa: FrontStack[A], z: => B)(f: (A, => B) => B) =
fa.toBackStack.bqFoldRight(z)(
(a, z) => f(a, z),
(iaa, z) => iaa.foldRight(z)(f(_, _)),
)
override def length[A](fa: FrontStack[A]) = fa.length
}
implicit def `FrontStack Order`[A: Order]: Order[FrontStack[A]] = {
import scalaz.std.iterable._
orderBy(fs => toIterableForScalazInstances(fs.iterator), true)

View File

@ -3,6 +3,7 @@
package com.daml.lf.data
import com.daml.scalautil.FoldableContravariant
import com.daml.scalautil.Statement.discard
import ScalazEqual.{equalBy, orderBy, toIterableForScalazInstances}
@ -393,6 +394,12 @@ object ImmArray extends ImmArrayInstances {
}
.map(_.toImmArray)
}
override def foldLeft[A, B](fa: ImmArray[A], z: B)(f: (B, A) => B) =
fa.foldLeft(z)(f)
override def foldRight[A, B](fa: ImmArray[A], z: => B)(f: (A, => B) => B) =
fa.foldRight(z)(f(_, _))
}
implicit def immArrayOrderInstance[A: Order]: Order[ImmArray[A]] = {
@ -429,12 +436,13 @@ object ImmArray extends ImmArrayInstances {
object ImmArraySeq extends ImmArraySeqCompanion {
val Empty: ImmArraySeq[Nothing] = ImmArray.Empty.toSeq
implicit val `immArraySeq Traverse instance`: Traverse[ImmArraySeq] = new Traverse[ImmArraySeq]
with Foldable.FromFoldr[ImmArraySeq] {
with Foldable.FromFoldr[ImmArraySeq]
with FoldableContravariant[ImmArraySeq, ImmArray] {
override def map[A, B](fa: ImmArraySeq[A])(f: A => B) = fa.toImmArray.map(f).toSeq
override def foldLeft[A, B](fa: ImmArraySeq[A], z: B)(f: (B, A) => B) =
fa.foldLeft(z)(f)
override def foldRight[A, B](fa: ImmArraySeq[A], z: => B)(f: (A, => B) => B) =
fa.foldRight(z)(f(_, _))
protected[this] override def Y = Foldable[ImmArray]
protected[this] override def ctmap[A](xa: ImmArraySeq[A]) = xa.toImmArray
override def traverseImpl[F[_], A, B](
immArr: ImmArraySeq[A]
)(f: A => F[B])(implicit F: Applicative[F]): F[ImmArraySeq[B]] = {

View File

@ -75,6 +75,10 @@ class FrontStackSpec
"Traverse instance" should {
checkLaws(ScalazProperties.traverse.laws[FrontStack])
"reconstruct itself with foldRight" in forAll { fs: FrontStack[Int] =>
scalaz.Foldable[FrontStack].foldRight(fs, FrontStack.empty[Int])(_ +: _) should ===(fs)
}
}
"Equal instance" should {

View File

@ -0,0 +1,60 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.scalautil
import scalaz.{Monoid, Foldable, Semigroup}
import FoldableContravariant._
// Specialized overrides for when a Foldable wraps another Foldable.
// If you need to hand-write some of these, just mix in the traits you
// don't want to hand-write.
private[daml] trait FoldableContravariant[X[_], Y[_]]
extends CoreOps[X, Y]
with Semigroupoids[X, Y]
with Conversions[X, Y]
with Lookups[X, Y]
private[daml] object FoldableContravariant {
trait CtMap[X[_], Y[_]] {
protected[this] def Y: Foldable[Y]
protected[this] def ctmap[A](ax: X[A]): Y[A]
}
// non-derived combinators
trait CoreOps[X[_], Y[_]] extends Foldable[X] with CtMap[X, Y] {
override final def foldLeft[A, B](xa: X[A], z: B)(f: (B, A) => B) = Y.foldLeft(ctmap(xa), z)(f)
override final def foldRight[A, B](xa: X[A], z: => B)(f: (A, => B) => B) =
Y.foldRight(ctmap(xa), z)(f)
override final def foldMap[A, Z: Monoid](xa: X[A])(f: A => Z) = Y.foldMap(ctmap(xa))(f)
}
// plays on functions from the semigroupoids library
trait Semigroupoids[X[_], Y[_]] extends Foldable[X] with CtMap[X, Y] {
override final def foldMapRight1Opt[A, B](xa: X[A])(z: A => B)(f: (A, => B) => B) =
Y.foldMapRight1Opt(ctmap(xa))(z)(f)
override final def foldMapLeft1Opt[A, B](xa: X[A])(z: A => B)(f: (B, A) => B) =
Y.foldMapLeft1Opt(ctmap(xa))(z)(f)
override final def foldMap1Opt[A, B: Semigroup](xa: X[A])(f: A => B) =
Y.foldMap1Opt(ctmap(xa))(f)
}
// to (collection type) converters
trait Conversions[X[_], Y[_]] extends Foldable[X] with CtMap[X, Y] {
override final def toStream[A](xa: X[A]) = Y.toStream(ctmap(xa))
override final def toSet[A](xa: X[A]) = Y.toSet(ctmap(xa))
override final def toList[A](xa: X[A]) = Y.toList(ctmap(xa))
override final def toVector[A](xa: X[A]) = Y.toVector(ctmap(xa))
override final def toIList[A](xa: X[A]) = Y.toIList(ctmap(xa))
override final def toEphemeralStream[A](xa: X[A]) =
Y.toEphemeralStream(ctmap(xa))
}
// list-like operations
trait Lookups[X[_], Y[_]] extends Foldable[X] with CtMap[X, Y] {
override final def index[A](xa: X[A], i: Int) = Y.index(ctmap(xa), i)
override final def length[A](xa: X[A]) = Y.length(ctmap(xa))
override final def all[A](xa: X[A])(p: A => Boolean): Boolean = Y.all(ctmap(xa))(p)
override final def any[A](xa: X[A])(p: A => Boolean): Boolean = Y.any(ctmap(xa))(p)
}
}

View File

@ -6,10 +6,12 @@ package com.daml.scalautil.nonempty
import scala.collection.compat._
import scala.collection.{immutable => imm}, imm.Map, imm.Set
import scalaz.Id.Id
import scalaz.{Foldable, Foldable1, Monoid, OneAnd, Semigroup, Traverse}
import scalaz.{Foldable, Foldable1, OneAnd, Semigroup, Traverse}
import scalaz.Leibniz, Leibniz.===
import scalaz.Liskov, Liskov.<~<
import scalaz.syntax.std.option._
import com.daml.scalautil.FoldableContravariant
import NonEmptyCollCompat._
/** The visible interface of [[NonEmpty]]; use that value to access
@ -169,7 +171,7 @@ sealed abstract class NonEmptyCollInstances extends NonEmptyCollInstances0 {
sealed abstract class NonEmptyCollInstances0 {
implicit def foldable1[F[_]](implicit F: Foldable[F]): Foldable1[NonEmptyF[F, *]] =
NonEmpty.substF(new Foldable1[F] {
NonEmpty.substF(new Foldable1[F] with FoldableContravariant[F, F] {
private[this] def errEmpty(fa: F[_]) =
throw new IllegalArgumentException(
s"empty structure coerced to non-empty: $fa: ${fa.getClass.getSimpleName}"
@ -187,12 +189,9 @@ sealed abstract class NonEmptyCollInstances0 {
override def foldMapLeft1[A, B](fa: F[A])(z: A => B)(f: (B, A) => B) =
assertNE(fa, F.foldMapLeft1Opt(fa)(z)(f))
override def foldMap[A, B: Monoid](fa: F[A])(f: A => B) = F.foldMap(fa)(f)
protected[this] override def Y = F
override def foldRight[A, B](fa: F[A], z: => B)(f: (A, => B) => B) = F.foldRight(fa, z)(f)
override def foldLeft[A, B](fa: F[A], z: B)(f: (B, A) => B) =
F.foldLeft(fa, z)(f)
protected[this] override def ctmap[A](xa: F[A]) = xa
})
import scala.language.implicitConversions