Skip to content

Instantly share code, notes, and snippets.

@shajra
Last active March 27, 2016 22:28
Show Gist options
  • Save shajra/966c3c522ae5425b865d to your computer and use it in GitHub Desktop.
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.
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
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