Last active
March 27, 2016 22:28
-
-
Save shajra/966c3c522ae5425b865d to your computer and use it in GitHub Desktop.
I've found this useful for accessing nested data with accumulated error reporting.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package shajra.extn.scalaz | |
import scalaz. | |
{ \/, Arrow, Applicative, Foldable, Functor, IsomorphismApplicative, | |
IsomorphismFunctor, IsomorphismMonoid, IsomorphismPlus, | |
IsomorphismSemigroup, Kleisli, Maybe, Monoid, NonEmptyList, Plus, | |
ProChoice, Semigroup, Validation } | |
import scalaz.Isomorphism. | |
{ <=>, <~>, <~~>, IsoBifunctorTemplate, IsoFunctorTemplate, IsoSet } | |
import scalaz.syntax.foldable._ | |
import scalaz.syntax.validation._ | |
final case class ReadV[I, E, O] | |
(kleisli: Kleisli[Validation[E, ?], I, O]) extends AnyVal { | |
def apply(i: I): Validation[E, O] = run(i) | |
def run(i: I): Validation[E, O] = kleisli run i | |
def mapE[EE](f: E => EE): ReadV[I, EE, O] = | |
ReadV(kleisli.mapK[Validation[EE, ?], O] { _ leftMap f }) | |
def nel: ReadV[I, NonEmptyList[E], O] = | |
mapE { NonEmptyList(_) } | |
} | |
trait ReadVFunctions { | |
def readV[I, E, O] | |
(f: I => Validation[E, O]) | |
: ReadV[I, E, O] = | |
ReadV(Kleisli[Validation[E, ?], I, O](f)) | |
def read[I, E, O](f: I => O ): ReadV[I, E, O] = | |
readV { f(_).success } | |
def check[I, E] | |
(fault: I => E) | |
(isTrue: I => Boolean) | |
: ReadV[I, E, I] = | |
readV { i => if (isTrue(i)) i.success else fault(i).failure } | |
def exactlyOne[F[_] : Foldable, I, E] | |
(fault: F[I] => E) | |
: ReadV[F[I], E, I] = | |
readV { fi => | |
if (fi.count != 1) | |
fault(fi).failure | |
else | |
fi index 0 map { _.success } getOrElse fault(fi).failure | |
} | |
def maybeOne[F[_] : Foldable, I, E] | |
(fault: F[I] => E) | |
: ReadV[F[I], E, Maybe[I]] = | |
readV { fi => | |
if (fi.count > 1) | |
fault(fi).failure | |
else | |
Maybe fromOption (fi index 0) success | |
} | |
def nonEmpty[F[_] : Foldable, I, E] | |
(fault: F[I] => E) | |
: ReadV[F[I], E, NonEmptyList[I]] = | |
readV { fi => | |
fi.foldMap1Opt { NonEmptyList(_) } | |
.map { _.success } | |
.getOrElse(fault(fi).failure) | |
} | |
} | |
// DESIGN: this is a false positive warning from Wartremover | |
@SuppressWarnings( | |
Array("org.brianmckenna.wartremover.warts.ExplicitImplicitTypes")) | |
trait ReadVInstances2 { | |
implicit def monoid[I, E : Semigroup, O : Monoid]: Monoid[ReadV[I, E, O]] = | |
new IsomorphismMonoid[ReadV[I, E, O], Kleisli[Validation[E, ?], I, O]] { | |
val G = Kleisli.kleisliMonoid[Validation[E, ?], I, O] | |
val iso = isoSet[I, E, O] | |
} | |
implicit def functor[I, E]: Functor[ReadV[I, E, ?]] = | |
new IsomorphismFunctor[ReadV[I, E, ?], Kleisli[Validation[E, ?], I, ?]] { | |
val G = Kleisli.kleisliFunctor[Validation[E, ?], I] | |
val iso = isoFunctor[I, E] | |
} | |
def isoFunctor[I, E]: ReadV[I, E, ?] <~> Kleisli[Validation[E, ?], I, ?] = | |
new IsoFunctorTemplate[ReadV[I, E, ?], Kleisli[Validation[E, ?], I, ?]] { | |
def to[A](r: ReadV[I, E, A]) = r.kleisli | |
def from[A](k: Kleisli[Validation[E, ?], I, A]) = ReadV(k) | |
} | |
def isoSet[I, E, O]: ReadV[I, E, O] <=> Kleisli[Validation[E, ?], I, O] = | |
new IsoSet[ReadV[I, E, O], Kleisli[Validation[E, ?], I, O]] { | |
def to = { _.kleisli } | |
def from = { ReadV(_) } | |
} | |
def isoBifunctor[E] | |
: ReadV[?, E, ?] <~~> Kleisli[Lambda[a => Validation[E, a]], ?, ?] = | |
new IsoBifunctorTemplate | |
[ReadV[?, E, ?], Kleisli[Lambda[a => Validation[E, a]], ?, ?]] { | |
def to[A, B](r: ReadV[A, E, B]) = | |
r.kleisli | |
def from[A, B](k: Kleisli[Lambda[a => Validation[E, a]], A, B]) = | |
ReadV(k) | |
} | |
} | |
// DESIGN: this is a false positive warning from Wartremover | |
@SuppressWarnings( | |
Array("org.brianmckenna.wartremover.warts.ExplicitImplicitTypes")) | |
trait ReadVInstances1 extends ReadVInstances2 { | |
implicit def semigroup[I, E : Semigroup, O : Semigroup] | |
: Semigroup[ReadV[I, E, O]] = | |
new IsomorphismSemigroup[ReadV[I, E, O], Kleisli[Validation[E, ?], I, O]] { | |
val G = Kleisli.kleisliSemigroup[Validation[E, ?], I, O] | |
val iso = isoSet[I, E, O] | |
} | |
implicit def applicative[I, E : Semigroup]: Applicative[ReadV[I, E, ?]] = | |
new IsomorphismApplicative | |
[ReadV[I, E, ?], Kleisli[Validation[E, ?], I, ?]] { | |
val G = Kleisli.kleisliApplicative[Validation[E, ?], I] | |
val iso = isoFunctor[I, E] | |
} | |
implicit def plus[I, E]: Plus[ReadV[I, E, ?]] = | |
new IsomorphismPlus[ReadV[I, E, ?], Kleisli[Validation[E, ?], I, ?]] { | |
val G = Kleisli.kleisliPlus[Validation[E, ?], I] | |
val iso = isoFunctor[I, E] | |
} | |
implicit def proChoiceWithArrow[E : Semigroup] | |
: ProChoice[ReadV[?, E, ?]] with Arrow[ReadV[?, E, ?]] = | |
new Arrow[ReadV[?, E, ?]] | |
with IsomorphismProChoice | |
[ReadV[?, E, ?], Kleisli[Lambda[a => Validation[E, a]], ?, ?]] { | |
val G = Kleisli.kleisliProChoice[Validation[E, ?]] | |
val iso = isoBifunctor[E] | |
def arr[A, B](f: A => B) = ReadV.read(f) | |
def id[A] = ReadV.read(identity) | |
def first[A, B, C](f: ReadV[A, E, B]): ReadV[(A, C), E, (B, C)] = | |
ReadV.readV[(A, C), E, (B, C)] { case (a, c) => f run a map { (_, c) } } | |
def compose[A, B, C](f: ReadV[B, E, C], g: ReadV[A, E, B]) | |
: ReadV[A, E, C] = | |
ReadV( | |
validation(disjunction(f.kleisli).compose(disjunction(g.kleisli)))) | |
def disjunction[A, B](k: Kleisli[Validation[E, ?], A, B]) = | |
k.mapK[E \/ ?, B] { _.disjunction } | |
def validation[A, B](k: Kleisli[E \/ ?, A, B]) = | |
k.mapK[Validation[E, ?], B] { _.validation } | |
} | |
} | |
trait ReadVInstances extends ReadVInstances1 | |
object ReadVFunctions extends ReadVFunctions | |
object ReadVInstances extends ReadVInstances | |
object ReadV extends ReadVFunctions with ReadVInstances |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package shajra.example | |
import shajra.app.{ Check, SimpleApp, ProgName, ProgDesc } | |
import shajra.app.ConfigRead._ | |
import scalaz.Maybe | |
import scalaz.concurrent.Task | |
import scalaz.syntax.apply._ | |
import scalaz.syntax.compose._ | |
object ExampleApp extends SimpleApp[Settings] { | |
def progName = ProgName("hello") | |
def progDesc = ProgDesc("Hello World Application") | |
def configReader = | |
sub("example") >>> | |
( sub("consul") >>> | |
( required[String]("host").map(Host) |@| | |
checkedRequired[Int]("port")( | |
Check.failIf[Int](_ == 100)(p => s"bad port: ${p}") | |
).map(Port) | |
)(ConsulSettings) |@| | |
required[String]("user").map(User) |@| | |
lookup[List[List[String]]]("mode").map(Mode) | |
)(Settings) | |
def run(settings: Settings) = Task.delay { println(settings) } | |
} | |
final case class Settings(consul: ConsulSettings, user: User, mode: Mode) | |
final case class ConsulSettings(host: Host, port: Port) | |
final case class Host(name: String) extends AnyVal | |
final case class Port(num: Int) extends AnyVal | |
final case class User(name: String) extends AnyVal | |
final case class Mode(name: scalaz.Maybe[List[List[String]]]) extends AnyVal |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment