Skip to content

Commit

Permalink
rename traverse1 to nonEmptyTraverse
Browse files Browse the repository at this point in the history
  • Loading branch information
Luka Jacobowitz committed Jun 8, 2017
1 parent 7f55edc commit 1535936
Show file tree
Hide file tree
Showing 21 changed files with 107 additions and 106 deletions.
10 changes: 5 additions & 5 deletions core/src/main/scala/cats/Composed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ private[cats] trait ComposedTraverse[F[_], G[_]] extends Traverse[λ[α => F[G[
F.traverse(fga)(ga => G.traverse(ga)(f))
}

private[cats] trait ComposedTraverse1[F[_], G[_]] extends Traverse1[λ[α => F[G[α]]]] with ComposedTraverse[F, G] with ComposedReducible[F, G] {
def F: Traverse1[F]
def G: Traverse1[G]
private[cats] trait ComposedNonEmptyTraverse[F[_], G[_]] extends NonEmptyTraverse[λ[α => F[G[α]]]] with ComposedTraverse[F, G] with ComposedReducible[F, G] {
def F: NonEmptyTraverse[F]
def G: NonEmptyTraverse[G]

override def traverse1[H[_]: Apply, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] =
F.traverse1(fga)(ga => G.traverse1(ga)(f))
override def nonEmptyTraverse[H[_]: Apply, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] =
F.nonEmptyTraverse(fga)(ga => G.nonEmptyTraverse(ga)(f))
}

private[cats] trait ComposedTraverseFilter[F[_], G[_]] extends TraverseFilter[λ[α => F[G[α]]]] with ComposedTraverse[F, G] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package cats
import simulacrum.typeclass

/**
* Traverse1, also known as Traversable1.
* NonEmptyTraverse, also known as Traversable1.
*
* `Traverse1` is like a non-empty `Traverse`. In addition to the traverse and sequence
* methods it provides traverse1 and sequence1 methods which require an `Apply` instance instead of `Applicative`.
* `NonEmptyTraverse` is like a non-empty `Traverse`. In addition to the traverse and sequence
* methods it provides nonEmptyTraverse and nonEmptySequence methods which require an `Apply` instance instead of `Applicative`.
*/
@typeclass trait Traverse1[F[_]] extends Traverse[F] with Reducible[F] { self =>
@typeclass trait NonEmptyTraverse[F[_]] extends Traverse[F] with Reducible[F] { self =>

/**
* Given a function which returns a G effect, thread this effect
Expand All @@ -20,11 +20,11 @@ import simulacrum.typeclass
* scala> import cats.implicits._
* scala> import cats.data.NonEmptyList
* scala> def countWords(words: List[String]): Map[String, Int] = words.groupBy(identity).mapValues(_.length)
* scala> NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")).traverse1(countWords)
* scala> NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do")).nonEmptyTraverse(countWords)
* res0: Map[String,cats.data.NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1))
* }}}
*/
def traverse1[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
def nonEmptyTraverse[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]]

/**
* Thread all the G effects through the F structure to invert the
Expand All @@ -36,31 +36,31 @@ import simulacrum.typeclass
* scala> import cats.data.NonEmptyList
* scala> val x = NonEmptyList.of(Map("do" -> 1, "you" -> 1), Map("do" -> 2, "you" -> 1))
* scala> val y = NonEmptyList.of(Map("How" -> 3, "do" -> 1, "you" -> 1), Map[String,Int]())
* scala> x.sequence1
* scala> x.nonEmptySequence
* res0: Map[String,NonEmptyList[Int]] = Map(do -> NonEmptyList(1, 2), you -> NonEmptyList(1, 1))
* scala> y.sequence1
* scala> y.nonEmptySequence
* res1: Map[String,NonEmptyList[Int]] = Map()
* }}}
*/
def sequence1[G[_]: Apply, A](fga: F[G[A]]): G[F[A]] =
traverse1(fga)(identity)
def nonEmptySequence[G[_]: Apply, A](fga: F[G[A]]): G[F[A]] =
nonEmptyTraverse(fga)(identity)


/**
* A traverse1 followed by flattening the inner result.
* A nonEmptyTraverse followed by flattening the inner result.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.data.NonEmptyList
* scala> def countWords(words: List[String]): Map[String, Int] = words.groupBy(identity).mapValues(_.length)
* scala> val x = NonEmptyList.of(List("How", "do", "you", "fly"), List("What", "do", "you", "do"))
* scala> x.flatTraverse1(_.groupByNel(identity))
* scala> x.nonEmptyFlatTraverse(_.groupByNel(identity))
* res0: Map[String,cats.data.NonEmptyList[String]] = Map(do -> NonEmptyList(do, do, do), you -> NonEmptyList(you, you))
* }}}
*/
def flatTraverse1[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Apply[G], F: FlatMap[F]): G[F[B]] =
G.map(traverse1(fa)(f))(F.flatten)
def nonEmptyFlatTraverse[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Apply[G], F: FlatMap[F]): G[F[B]] =
G.map(nonEmptyTraverse(fa)(f))(F.flatten)

/**
* Thread all the G effects through the F structure and flatten to invert the
Expand All @@ -72,22 +72,22 @@ import simulacrum.typeclass
* scala> import cats.data.NonEmptyList
* scala> val x = NonEmptyList.of(Map(0 ->NonEmptyList.of(1, 2)), Map(0 -> NonEmptyList.of(3)))
* scala> val y: NonEmptyList[Map[Int, NonEmptyList[Int]]] = NonEmptyList.of(Map(), Map(1 -> NonEmptyList.of(3)))
* scala> x.flatSequence1
* scala> x.nonEmptyFlatSequence
* res0: Map[Int,cats.data.NonEmptyList[Int]] = Map(0 -> NonEmptyList(1, 2, 3))
* scala> y.flatSequence1
* scala> y.nonEmptyFlatSequence
* res1: Map[Int,cats.data.NonEmptyList[Int]] = Map()
* }}}
*/
def flatSequence1[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] =
G.map(traverse1(fgfa)(identity))(F.flatten)
def nonEmptyFlatSequence[G[_], A](fgfa: F[G[F[A]]])(implicit G: Apply[G], F: FlatMap[F]): G[F[A]] =
G.map(nonEmptyTraverse(fgfa)(identity))(F.flatten)

override def traverse[G[_] : Applicative, A, B](fa: F[A])(f: (A) => G[B]): G[F[B]] =
traverse1(fa)(f)
nonEmptyTraverse(fa)(f)

def compose[G[_]: Traverse1]: Traverse1[λ[α => F[G[α]]]] =
new ComposedTraverse1[F, G] {
def compose[G[_]: NonEmptyTraverse]: NonEmptyTraverse[λ[α => F[G[α]]]] =
new ComposedNonEmptyTraverse[F, G] {
val F = self
val G = Traverse1[G]
val G = NonEmptyTraverse[G]
}


Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/Reducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,17 @@ import simulacrum.typeclass
* available for `G` and want to take advantage of short-circuiting
* the traversal.
*/
def traverse1_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] =
def nonEmptyTraverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] =
G.map(reduceLeftTo(fa)(f)((x, y) => G.map2(x, f(y))((_, b) => b)))(_ => ())

/**
* Sequence `F[G[A]]` using `Apply[G]`.
*
* This method is similar to [[Foldable.sequence_]] but requires only
* an [[Apply]] instance for `G` instead of [[Applicative]]. See the
* [[traverse1_]] documentation for a description of the differences.
* [[nonEmptyTraverse_]] documentation for a description of the differences.
*/
def sequence1_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] =
def nonEmptySequence_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] =
G.map(reduceLeft(fga)((x, y) => G.map2(x, y)((_, b) => b)))(_ => ())

def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] =
Expand Down
14 changes: 7 additions & 7 deletions core/src/main/scala/cats/data/Nested.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ private[data] sealed abstract class NestedInstances extends NestedInstances0 {
val FG: TraverseFilter[λ[α => F[G[α]]]] = Traverse[F].composeFilter[G]
}

implicit def catsDataTraverse1ForNested[F[_]: Traverse1, G[_]: Traverse1]: Traverse1[Nested[F, G, ?]] =
new NestedTraverse1[F, G] {
val FG: Traverse1[λ[α => F[G[α]]]] = Traverse1[F].compose[G]
implicit def catsDataNonEmptyTraverseForNested[F[_]: NonEmptyTraverse, G[_]: NonEmptyTraverse]: NonEmptyTraverse[Nested[F, G, ?]] =
new NestedNonEmptyTraverse[F, G] {
val FG: NonEmptyTraverse[λ[α => F[G[α]]]] = NonEmptyTraverse[F].compose[G]
}
}

Expand Down Expand Up @@ -238,11 +238,11 @@ private[data] trait NestedReducible[F[_], G[_]] extends Reducible[Nested[F, G, ?
FG.reduceRightTo(fga.value)(f)(g)
}

private[data] trait NestedTraverse1[F[_], G[_]] extends Traverse1[Nested[F, G, ?]] with NestedTraverse[F, G] with NestedReducible[F, G] {
def FG: Traverse1[λ[α => F[G[α]]]]
private[data] trait NestedNonEmptyTraverse[F[_], G[_]] extends NonEmptyTraverse[Nested[F, G, ?]] with NestedTraverse[F, G] with NestedReducible[F, G] {
def FG: NonEmptyTraverse[λ[α => F[G[α]]]]

override def traverse1[H[_]: Apply, A, B](fga: Nested[F, G, A])(f: A => H[B]): H[Nested[F, G, B]] =
Apply[H].map(FG.traverse1(fga.value)(f))(Nested(_))
override def nonEmptyTraverse[H[_]: Apply, A, B](fga: Nested[F, G, A])(f: A => H[B]): H[Nested[F, G, B]] =
Apply[H].map(FG.nonEmptyTraverse(fga.value)(f))(Nested(_))
}

private[data] trait NestedContravariant[F[_], G[_]] extends Contravariant[Nested[F, G, ?]] {
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,9 @@ object NonEmptyList extends NonEmptyListInstances {
private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 {

implicit val catsDataInstancesForNonEmptyList: SemigroupK[NonEmptyList] with Reducible[NonEmptyList]
with Comonad[NonEmptyList] with Traverse1[NonEmptyList] with Monad[NonEmptyList] =
with Comonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] with Monad[NonEmptyList] =
new NonEmptyReducible[NonEmptyList, List] with SemigroupK[NonEmptyList] with Comonad[NonEmptyList]
with Monad[NonEmptyList] with Traverse1[NonEmptyList] {
with Monad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] {

def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] =
a concat b
Expand All @@ -394,7 +394,7 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0

def extract[A](fa: NonEmptyList[A]): A = fa.head

def traverse1[G[_], A, B](nel: NonEmptyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyList[B]] =
def nonEmptyTraverse[G[_], A, B](nel: NonEmptyList[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyList[B]] =
Foldable[List].reduceRightToOption[A, G[List[B]]](nel.tail)(a => G.map(f(a))(_ :: Nil)) { (a, lglb) =>
G.map2Eval(f(a), lglb)(_ :: _)
}.map {
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,9 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) extends AnyVal
private[data] sealed trait NonEmptyVectorInstances {

implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector] with Reducible[NonEmptyVector]
with Comonad[NonEmptyVector] with Traverse1[NonEmptyVector] with Monad[NonEmptyVector] =
with Comonad[NonEmptyVector] with NonEmptyTraverse[NonEmptyVector] with Monad[NonEmptyVector] =
new NonEmptyReducible[NonEmptyVector, Vector] with SemigroupK[NonEmptyVector] with Comonad[NonEmptyVector]
with Monad[NonEmptyVector] with Traverse1[NonEmptyVector] {
with Monad[NonEmptyVector] with NonEmptyTraverse[NonEmptyVector] {

def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] =
a concatNev b
Expand Down Expand Up @@ -226,7 +226,7 @@ private[data] sealed trait NonEmptyVectorInstances {

def extract[A](fa: NonEmptyVector[A]): A = fa.head

def traverse1[G[_], A, B](nel: NonEmptyVector[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyVector[B]] =
def nonEmptyTraverse[G[_], A, B](nel: NonEmptyVector[A])(f: A => G[B])(implicit G: Apply[G]): G[NonEmptyVector[B]] =
Foldable[Vector].reduceRightToOption[A, G[Vector[B]]](nel.tail)(a => G.map(f(a))(_ +: Vector.empty)) { (a, lglb) =>
G.map2Eval(f(a), lglb)(_ +: _)
}.map {
Expand Down
7 changes: 4 additions & 3 deletions core/src/main/scala/cats/data/OneAnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,16 @@ private[data] trait OneAndLowPriority2 extends OneAndLowPriority1 {
}

private[data] trait OneAndLowPriority3 extends OneAndLowPriority2 {
implicit def catsDataTraverse1ForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): Traverse1[OneAnd[F, ?]] =
new NonEmptyReducible[OneAnd[F, ?], F] with Traverse1[OneAnd[F, ?]] {
def traverse1[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = {
implicit def catsDataNonEmptyTraverseForOneAnd[F[_]](implicit F: Traverse[F], F2: MonadCombine[F]): NonEmptyTraverse[OneAnd[F, ?]] =
new NonEmptyReducible[OneAnd[F, ?], F] with NonEmptyTraverse[OneAnd[F, ?]] {
def nonEmptyTraverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = {
import cats.syntax.cartesian._

fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F)
.reduceLeft(((acc, a) => (acc |@| a).map((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y))))
}


override def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = {
G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value
}
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ package object cats {
* encodes pure unary function application.
*/
type Id[A] = A
implicit val catsInstancesForId: Bimonad[Id] with Monad[Id] with Traverse1[Id] =
new Bimonad[Id] with Monad[Id] with Traverse1[Id] {
implicit val catsInstancesForId: Bimonad[Id] with Monad[Id] with NonEmptyTraverse[Id] =
new Bimonad[Id] with Monad[Id] with NonEmptyTraverse[Id] {
def pure[A](a: A): A = a
def extract[A](a: A): A = a
def flatMap[A, B](a: A)(f: A => B): B = f(a)
Expand All @@ -51,7 +51,7 @@ package object cats {
def foldLeft[A, B](a: A, b: B)(f: (B, A) => B) = f(b, a)
def foldRight[A, B](a: A, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
f(a, lb)
def traverse1[G[_], A, B](a: A)(f: A => G[B])(implicit G: Apply[G]): G[B] =
def nonEmptyTraverse[G[_], A, B](a: A)(f: A => G[B])(implicit G: Apply[G]): G[B] =
f(a)
override def foldMap[A, B](fa: Id[A])(f: A => B)(implicit B: Monoid[B]): B = f(fa)
override def reduce[A](fa: Id[A])(implicit A: Semigroup[A]): A =
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ trait AllSyntax
with StrongSyntax
with TraverseFilterSyntax
with TraverseSyntax
with Traverse1Syntax
with NonEmptyTraverseSyntax
with TupleSyntax
with ValidatedSyntax
with VectorSyntax
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/scala/cats/syntax/nonEmptyTraverse.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package cats
package syntax

trait NonEmptyTraverseSyntax extends NonEmptyTraverse.ToNonEmptyTraverseOps
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ package object syntax {
object strong extends StrongSyntax
object monadTrans extends MonadTransSyntax
object traverse extends TraverseSyntax
object traverse1 extends Traverse1Syntax
object nonEmptyTraverse extends NonEmptyTraverseSyntax
object traverseFilter extends TraverseFilterSyntax
object tuple extends TupleSyntax
object validated extends ValidatedSyntax
Expand Down
4 changes: 0 additions & 4 deletions core/src/main/scala/cats/syntax/traverse1.scala

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package cats.laws


import cats.{Apply, Id, Semigroup, Traverse1}
import cats.{Apply, Id, Semigroup, NonEmptyTraverse}
import cats.data.{Const, Nested}
import cats.syntax.traverse1._
import cats.syntax.nonEmptyTraverse._
import cats.syntax.reducible._

trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] {
implicit override def F: Traverse1[F]
trait NonEmptyTraverseLaws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] {
implicit override def F: NonEmptyTraverse[F]

def traverse1Identity[A, B](fa: F[A], f: A => B): IsEq[F[B]] = {
fa.traverse1[Id, B](f) <-> F.map(fa)(f)
def nonEmptyTraverseIdentity[A, B](fa: F[A], f: A => B): IsEq[F[B]] = {
fa.nonEmptyTraverse[Id, B](f) <-> F.map(fa)(f)
}

def traverse1SequentialComposition[A, B, C, M[_], N[_]](
def nonEmptyTraverseSequentialComposition[A, B, C, M[_], N[_]](
fa: F[A],
f: A => M[B],
g: B => N[C]
Expand All @@ -22,12 +22,12 @@ trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] {
M: Apply[M]
): IsEq[Nested[M, N, F[C]]] = {

val lhs = Nested(M.map(fa.traverse1(f))(fb => fb.traverse1(g)))
val rhs = fa.traverse1[Nested[M, N, ?], C](a => Nested(M.map(f(a))(g)))
val lhs = Nested(M.map(fa.nonEmptyTraverse(f))(fb => fb.nonEmptyTraverse(g)))
val rhs = fa.nonEmptyTraverse[Nested[M, N, ?], C](a => Nested(M.map(f(a))(g)))
lhs <-> rhs
}

def traverse1ParallelComposition[A, B, M[_], N[_]](
def nonEmptyTraverseParallelComposition[A, B, M[_], N[_]](
fa: F[A],
f: A => M[B],
g: A => N[B]
Expand All @@ -52,22 +52,22 @@ trait Traverse1Laws[F[_]] extends TraverseLaws[F] with ReducibleLaws[F] {
(M.product(mx, my), N.product(nx, ny))
}
}
val lhs: MN[F[B]] = fa.traverse1[MN, B](a => (f(a), g(a)))
val rhs: MN[F[B]] = (fa.traverse1(f), fa.traverse1(g))
val lhs: MN[F[B]] = fa.nonEmptyTraverse[MN, B](a => (f(a), g(a)))
val rhs: MN[F[B]] = (fa.nonEmptyTraverse(f), fa.nonEmptyTraverse(g))
lhs <-> rhs
}

def reduceMapDerived[A, B](
fa: F[A],
f: A => B
)(implicit B: Semigroup[B]): IsEq[B] = {
val lhs: B = fa.traverse1[Const[B, ?], B](a => Const(f(a))).getConst
val lhs: B = fa.nonEmptyTraverse[Const[B, ?], B](a => Const(f(a))).getConst
val rhs: B = fa.reduceMap(f)
lhs <-> rhs
}
}

object Traverse1Laws {
def apply[F[_]](implicit ev: Traverse1[F]): Traverse1Laws[F] =
new Traverse1Laws[F] { def F: Traverse1[F] = ev }
object NonEmptyTraverseLaws {
def apply[F[_]](implicit ev: NonEmptyTraverse[F]): NonEmptyTraverseLaws[F] =
new NonEmptyTraverseLaws[F] { def F: NonEmptyTraverse[F] = ev }
}
4 changes: 2 additions & 2 deletions laws/src/main/scala/cats/laws/ReducibleLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ trait ReducibleLaws[F[_]] extends FoldableLaws[F] {
fa.reduce <-> fa.reduceLeft(B.combine)

def traverseConsistent[G[_]: Applicative, A, B](fa: F[A], f: A => G[B]): IsEq[G[Unit]] =
fa.traverse1_(f) <-> fa.traverse_(f)
fa.nonEmptyTraverse_(f) <-> fa.traverse_(f)

def sequenceConsistent[G[_]: Applicative, A](fa: F[G[A]]): IsEq[G[Unit]] =
fa.sequence1_ <-> fa.sequence_
fa.nonEmptySequence_ <-> fa.sequence_

def sizeConsistent[A](fa: F[A]): IsEq[Long] =
fa.size <-> fa.reduceMap(_ => 1L)
Expand Down
Loading

0 comments on commit 1535936

Please sign in to comment.