Skip to content

Commit

Permalink
Replace scalaz with cats, prod code
Browse files Browse the repository at this point in the history
AIS-23733
  • Loading branch information
mahe-ymc committed Mar 16, 2022
1 parent fb5a59e commit eab0163
Show file tree
Hide file tree
Showing 23 changed files with 268 additions and 236 deletions.
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ val testDependencies = Seq(
libraryDependencies ++= Seq(
"com.chuusai" %% "shapeless" % "2.3.3",
"org.scala-lang.modules" %% "scala-xml" % "2.0.0",
"org.scalaz" %% "scalaz-core" % "7.2.31"
"org.scalaz" %% "scalaz-core" % "7.2.31",
"org.typelevel" %% "cats-core" % "2.6.1"
) ++ testDependencies ++ List(compilerPlugin("org.typelevel" % ("kind-projector_" + scalaVersion.value) % "0.13.0"))

wartremoverErrors ++= Warts.allBut(Wart.Any, Wart.Nothing, Wart.NonUnitStatements)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/ch/srf/xml/AttrValue.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package ch.srf.xml

private[xml] sealed trait AttrValue
private[xml] final case class AttrValue(unwrap: String) extends AnyVal
14 changes: 7 additions & 7 deletions src/main/scala/ch/srf/xml/CardinalityDecoder.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package ch.srf.xml

import scalaz.std.list.listInstance
import scalaz.std.option.optionInstance
import scalaz.syntax.traverse._
import scalaz.{NonEmptyList, Traverse, Applicative}
import cats.data.NonEmptyList
import cats.syntax.all._
import cats.{Traverse, Applicative}

private[xml] sealed trait CardinalityDecoder[F[_], Cy[_], X, A] {

Expand Down Expand Up @@ -33,9 +32,10 @@ private[xml] object CardinalityDecoder {

private def decodeTraverse[
F[_]: Applicative,
G[_]: Traverse, X, A](dec: X => Result[F, A],
xs: G[X]): Result[F, G[A]] = {
val pairs = xs.indexed.map { case (pos, x) => (x, pos + 1) }
G[_]: Traverse, X, A
](dec: X => Result[F, A],
xs: G[X]): Result[F, G[A]] = {
val pairs = xs.zipWithIndex.map { case (x, pos) => (x, pos + 1) }
pairs.traverse { case (e, pos) => dec(e).updatePos(pos) }
}

Expand Down
5 changes: 2 additions & 3 deletions src/main/scala/ch/srf/xml/Codec.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package ch.srf.xml

import cats.{Monad}
import ch.srf.xml.util.WrapGen
import scalaz.{Monad, \/}

import scala.annotation.implicitNotFound

@implicitNotFound("No codec found from ${X} to ${A}")
final class Codec[F[_], X, A](val decoder: Decoder[F, X, A], val encoder: Encoder[F, X, A]) {

def decode(x: X): F[String \/ A] =
def decode(x: X): F[String Either A] =
decoder.decode(x)

def encode(a: A): F[X] =
Expand Down
11 changes: 5 additions & 6 deletions src/main/scala/ch/srf/xml/CodecDsl.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package ch.srf.xml

import cats.data.NonEmptyList
import cats.Monad
import ch.srf.xml.util.CompactHList
import scalaz.std.list.listInstance
import scalaz.std.option.optionInstance
import scalaz.{@@, Monad, NonEmptyList}
import shapeless.{::, HList, HNil}

class CodecDsl[F[_]:Monad] extends EnsureOps {
Expand All @@ -17,13 +16,13 @@ class CodecDsl[F[_]:Monad] extends EnsureOps {
def oneOrMore[S, D, X, A](codec: XmlCodec[F, D, X, A]): XmlCodec[F, D, NonEmptyList[X], NonEmptyList[A]] =
XmlCodec.collection[F, NonEmptyList, D, X, A](codec, CardinalityDecoder.nel)

def attr(name: String): XmlCodec[F, String, String @@ AttrValue, String] =
def attr(name: String): XmlCodec[F, String, AttrValue, String] =
XmlCodec.attr(name)

def text: XmlCodec[F, Unit, String @@ TextValue, String] =
def text: XmlCodec[F, Unit, TextValue, String] =
XmlCodec.text

def nonEmptyText: XmlCodec[F, Unit, String @@ NonEmptyTextValue, String] =
def nonEmptyText: XmlCodec[F, Unit, NonEmptyTextValue, String] =
XmlCodec.nonEmptyText

private def elem[SC <: HList, C, A](name: String, children: SC)
Expand Down
79 changes: 40 additions & 39 deletions src/main/scala/ch/srf/xml/Decoder.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package ch.srf.xml

import cats.arrow.Compose
import cats.data.EitherT
import cats.data.Kleisli
import cats.syntax.all._
import cats.{Monad, Contravariant}
import ch.srf.xml.util.WrapGen
import scalaz.Isomorphism.<~~>
import scalaz.syntax.all._
import scalaz.syntax.std.option._
import scalaz.{Monad, \/, Contravariant, EitherT, Kleisli, Compose, ~~>}

import scala.annotation.implicitNotFound

@implicitNotFound("No decoder found from ${X} to ${A}")
final class Decoder[F[_], X, A] private (val decoder: Kleisli[EitherT[F, String, *], X, A])
extends AnyVal {
def decode(x: X): F[String \/ A] = decoder.run(x).run
def decode(x: X): F[String Either A] = decoder.run(x).value

//TODO just an alias for compose. Really needed?
def ~[B](other: Decoder[F, A, B])
Expand All @@ -31,49 +31,50 @@ trait DecoderLow extends DecoderLow2 {
}

object Decoder extends DecoderLow {
def apply[F[_], X, A](f: X => F[String \/ A]): Decoder[F, X, A] =
def apply[F[_], X, A](f: X => F[String Either A]): Decoder[F, X, A] =
new Decoder[F, X, A](Kleisli[EitherT[F, String, *], X, A](f andThen(EitherT(_))))

def fromDisjunction[F[_]:Monad, X, A](f: X => String \/ A): Decoder[F, X, A] =
apply(a => f(a).point[F])
def fromEither[F[_]:Monad, X, A](f: X => String Either A): Decoder[F, X, A] =
apply(a => f(a).pure[F])

def fromFunction[F[_]:Monad, X, A](f: X => A): Decoder[F, X, A] =
apply(f andThen(_.pure[String \/ *].pure[F]))
apply(f andThen(_.pure[String Either *].pure[F]))

def fromTryCatchNonFatal[F[_]:Monad, X, A](f: X => A): Decoder[F, X, A] =
apply(a => \/.fromTryCatchNonFatal(f(a)).leftMap(_.getMessage).point[F])
apply(a => Either.catchNonFatal(f(a)).leftMap(_.getMessage).pure[F])

def ensure[F[_]:Monad, A](e: Ensure[F, A]): Decoder[F, A, A] =
apply(a => e(a).map(_.<\/(a)))
apply(a => e(a).map(_.toLeft(a)))

def id[F[_]: Monad, E, I]: Decoder[F, I, I] =
fromFunction(identity)

def decoder2ToKleisli[F[_]]: Decoder[F, *, *] ~~> Kleisli[EitherT[F, String, *], *, *] =
new (Decoder[F, *, *] ~~> Kleisli[EitherT[F, String, *], *, *]) {
def apply[A, B](x: Decoder[F, A, B]) = x.decoder
}
def kleisliToDecoder[F[_]]: Kleisli[EitherT[F, String, *], *, *] ~~> Decoder[F, *, *] =
new (Kleisli[EitherT[F, String, *], *, *] ~~> Decoder[F, *, *]) {
def apply[A, B](x: Kleisli[EitherT[F, String, *], A, B]) = new Decoder(x)
}

def decoder2KleisliIsoBifunctor[F[_]]: Decoder[F, *, *] <~~> Kleisli[EitherT[F, String, *], *, *] =
new (Decoder[F, *, *] <~~> Kleisli[EitherT[F, String, *], *, *]) {
lazy val from = kleisliToDecoder[F]
lazy val to = decoder2ToKleisli[F]
}

//TODO we could provide all instances of Kleisli this way

implicit def decoder2ComposeInstance[F[_] : Monad]: Compose[Decoder[F, *, *]] =
Compose.fromIso(decoder2KleisliIsoBifunctor[F])

implicit def decoder2Monad[F[_] : Monad, A]: Monad[Decoder[F, A, *]] =
Monad.fromIso(decoder2KleisliIsoBifunctor[F].unlift1)

implicit def decoder2Contravariant[F[_], A]: Contravariant[Decoder[F, *, A]] =
Contravariant.fromIso[Decoder[F, *, A], Kleisli[EitherT[F, String, *], *, A]](
decoder2KleisliIsoBifunctor[F].unlift2[A]
)
def decoder2ToKleisli[F[_], X, A](dec: Decoder[F, X, A]): Kleisli[EitherT[F, String, *], X, A] =
dec.decoder

def kleisliToDecoder[F[_], X, A](x: Kleisli[EitherT[F, String, *], X, A]): Decoder[F, X, A] =
new Decoder(x)

implicit def decoder2ComposeInstance[F[_] : Monad]: Compose[Decoder[F, *, *]] = new Compose[Decoder[F, *, *]] {
override def compose[A, B, C](f: Decoder[F,B,C], g: Decoder[F,A,B]): Decoder[F,A,C] =
kleisliToDecoder(decoder2ToKleisli(f).compose(decoder2ToKleisli(g)))
}

implicit def decoder2Monad[F[_] : Monad, A]: Monad[Decoder[F, A, *]] = new Monad[Decoder[F, A, *]] {
override def flatMap[B, C](fa: Decoder[F,A,B])(f: B => Decoder[F,A,C]): Decoder[F,A,C] =
kleisliToDecoder(decoder2ToKleisli(fa).flatMap(f.andThen(decoder2ToKleisli)))

override def tailRecM[B, C](a: B)(f: B => Decoder[F,A,Either[B,C]]): Decoder[F,A,C] =
kleisliToDecoder(Monad[Kleisli[EitherT[F, String, *], A, *]].tailRecM(a)(f.andThen(decoder2ToKleisli)))

override def pure[B](x: B): Decoder[F,A,B] =
kleisliToDecoder(x.pure[Kleisli[EitherT[F, String, *], A, *]])

}

implicit def decoder2Contravariant[F[_], A]: Contravariant[Decoder[F, *, A]] = new Contravariant[Decoder[F, *, A]] {
override def contramap[B, C](fa: Decoder[F,B,A])(f: C => B): Decoder[F,C,A] =
kleisliToDecoder(decoder2ToKleisli(fa).local(f))
}

}
9 changes: 5 additions & 4 deletions src/main/scala/ch/srf/xml/DecoderDsl.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package ch.srf.xml

import cats.data.NonEmptyList
import cats.Monad
import ch.srf.xml.util.CompactHList
import scalaz.{@@, Monad, NonEmptyList}
import shapeless.{::, HList, HNil}

class DecoderDsl[F[_]:Monad] extends EnsureOps {
Expand All @@ -15,13 +16,13 @@ class DecoderDsl[F[_]:Monad] extends EnsureOps {
def oneOrMore[S, D, X, A](decoder: XmlDecoder[F, D, X, A]): XmlDecoder[F, D, NonEmptyList[X], NonEmptyList[A]] =
XmlDecoder.collection[F, NonEmptyList, D, X, A](decoder, CardinalityDecoder.nel)

def attr(name: String): XmlDecoder[F, String, String @@ AttrValue, String] =
def attr(name: String): XmlDecoder[F, String, AttrValue, String] =
XmlDecoder.attr(name)

def text: XmlDecoder[F, Unit, String @@ TextValue, String] =
def text: XmlDecoder[F, Unit, TextValue, String] =
XmlDecoder.text

def nonEmptyText: XmlDecoder[F, Unit, String @@ NonEmptyTextValue, String] =
def nonEmptyText: XmlDecoder[F, Unit, NonEmptyTextValue, String] =
XmlDecoder.nonEmptyText

private def elem[SC <: HList, C, A](name: String, children: SC)
Expand Down
8 changes: 4 additions & 4 deletions src/main/scala/ch/srf/xml/Descriptor.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ch.srf.xml

import scalaz._
import Scalaz._
import cats.kernel.Semigroup
import cats.syntax.all._

private[xml] final case class Descriptor[I](identifier: I, name: String)

Expand All @@ -14,7 +14,7 @@ private[xml] object Descriptor {
def elem(elemName: String): Descriptor[String] = Descriptor(elemName, elemName)

def or[I : Semigroup](one: Descriptor[I], two: Descriptor[I], separator: I) = Descriptor(
identifier = one.identifier |+| separator |+| two.identifier,
identifier = one.identifier |+| separator |+| two.identifier,
name = one.name |+| "|" |+| two.name
)
}
}
4 changes: 2 additions & 2 deletions src/main/scala/ch/srf/xml/Dsl.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ch.srf.xml

import scalaz.Id.Id
import scalaz.Monad
import cats.Id
import cats.Monad

class Dsl[F[_]:Monad] {

Expand Down
22 changes: 17 additions & 5 deletions src/main/scala/ch/srf/xml/Encoder.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package ch.srf.xml

import cats.Functor
import cats.syntax.all._
import cats.{Contravariant, Monad}
import ch.srf.xml.Encoder.fromFunction
import ch.srf.xml.util.WrapGen
import scalaz.syntax.all._
import scalaz.{Contravariant, Monad}

import scala.annotation.implicitNotFound

@implicitNotFound("No encoder found from ${A} to ${X}")
Expand All @@ -19,6 +19,7 @@ abstract class Encoder[F[_]:Monad, X, A] {
other.encode(b).flatMap(outer.encode)
}

final def xFunctor: Encoder.XFunctor[F, A, X] = new Encoder.XFunctor(this)
}

trait EncoderLow2 {
Expand All @@ -37,14 +38,26 @@ trait EncoderLow extends EncoderLow2 {

object Encoder extends EncoderLow {

sealed class XFunctor[F[_], A, X](val encoder: Encoder[F, X, A])

object XFunctor {
implicit def functorInstance[F[_]:Monad, X]: Functor[XFunctor[F, X, *]] = new Functor[XFunctor[F, X, *]] {
override def map[A, B](fa: XFunctor[F, X, A])(f: A => B): XFunctor[F,X,B] = new XFunctor[F, X, B](
new Encoder[F, B, X] {
override def encode(a: X): F[B] = fa.encoder.encode(a).map(f)
}
)
}
}

def apply[F[_]:Monad, X, A](enc: A => F[X]): Encoder[F, X, A] =
new Encoder[F, X, A] {
override def encode(a: A): F[X] =
enc(a)
}

def fromFunction[F[_]:Monad, X, A](enc: A => X): Encoder[F, X, A] =
Encoder(enc(_).point[F])
Encoder(enc(_).pure[F])

def id[F[_]: Monad, A]: Encoder[F, A, A] =
fromFunction(identity)
Expand All @@ -57,5 +70,4 @@ object Encoder extends EncoderLow {
e.encode(f(b))
}
}

}
11 changes: 5 additions & 6 deletions src/main/scala/ch/srf/xml/EncoderDsl.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package ch.srf.xml

import cats.data.NonEmptyList
import cats.Monad
import ch.srf.xml.util.CompactHList
import scalaz.std.list.listInstance
import scalaz.std.option.optionInstance
import scalaz.{@@, Monad, NonEmptyList}
import shapeless.{::, HList, HNil}

class EncoderDsl[F[_]:Monad] extends EnsureOps {
Expand All @@ -17,13 +16,13 @@ class EncoderDsl[F[_]:Monad] extends EnsureOps {
def oneOrMore[S, D, X, A](encoder: XmlEncoder[F, D, X, A]): XmlEncoder[F, D, NonEmptyList[X], NonEmptyList[A]] =
XmlEncoder.collection[F, NonEmptyList, D, X, A](encoder)

def attr(name: String): XmlEncoder[F, String, String @@ AttrValue, String] =
def attr(name: String): XmlEncoder[F, String, AttrValue, String] =
XmlEncoder.attr(name)

def text: XmlEncoder[F, Unit, String @@ TextValue, String] =
def text: XmlEncoder[F, Unit, TextValue, String] =
XmlEncoder.text

def nonEmptyText: XmlEncoder[F, Unit, String @@ NonEmptyTextValue, String] =
def nonEmptyText: XmlEncoder[F, Unit, NonEmptyTextValue, String] =
XmlEncoder.nonEmptyText

private def elem[SC <: HList, C, A](name: String, children: SC)
Expand Down
10 changes: 4 additions & 6 deletions src/main/scala/ch/srf/xml/EnsureOps.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package ch.srf.xml

import scalaz.syntax.applicative._
import scalaz.syntax.equal._
import scalaz.syntax.std.boolean._
import scalaz.{Applicative, Equal}
import cats.syntax.all._
import cats.{Applicative, Eq}

abstract class EnsureOps {

def check[F[_]:Applicative, A](f: A => Boolean, msg: A => String): Ensure[F, A] =
value => (!f(value)).option(msg(value)).point[F]
value => (if(f(value)) None else msg(value).some).pure[F]

def nonEmpty[F[_]:Applicative]: Ensure[F, String] =
check(!_.isEmpty, _ => "String must not be empty")

@SuppressWarnings(Array("org.wartremover.warts.ToString"))
def mustEqual[F[_]:Applicative, A](a: A)(implicit ev: Equal[A]): Ensure[F, A] =
def mustEqual[F[_]:Applicative, A](a: A)(implicit ev: Eq[A]): Ensure[F, A] =
check(_ === a, a => s"Value must equal '${a.toString}'")

}
Expand Down
Loading

0 comments on commit eab0163

Please sign in to comment.