Skip to content

Instantly share code, notes, and snippets.

@rossabaker
Created October 22, 2022 02:50
Show Gist options
  • Save rossabaker/503c28ef1cb2492f2db656084b11a16a to your computer and use it in GitHub Desktop.
Save rossabaker/503c28ef1cb2492f2db656084b11a16a to your computer and use it in GitHub Desktop.
package org.http4s
import cats._
import cats.mtl._
final class Service[F[_], -A, B] private (private[Service] val run: A => F[B]) {
def seal[B2 >: B](default: B2)(implicit F: Functor[F]): A => F[B2] =
a => F.map(run(a)) {
case null => default
case b => b
}
def lifted(implicit F: Functor[F]): A => F[Option[B]] =
a => F.map(run(a)) {
case null => None
case b => Some(b)
}
def map[C](f: B => C)(implicit F: Functor[F]): Service[F, A, C] =
new Service(a => F.map(run(a)) {
case null => null.asInstanceOf[C]
case b => f(b)
})
def flatMap[AA <: A, C](f: B => Service[F, AA, C])(implicit F: FlatMap[F]): Service[F, AA, C] =
new Service(a => F.flatMap(run(a)) {
case null => null.asInstanceOf[F[C]]
case b => f(b).run(a)
})
def local[C](f: C => A): Service[F, C, B] =
new Service(c => run(f(c)))
def orElse[AA <: A](that: Service[F, AA, B])(implicit F: Monad[F]): Service[F, AA, B] =
new Service(a => F.flatMap(run(a)) {
case null => that.run(a)
case b => F.pure(b)
})
}
object Service {
def pure[F[_], A, B](b: B)(implicit F: Applicative[F]): Service[F, A, B] =
new Service(Function.const(F.pure(b)))
def of[F[_], A, B](pf: PartialFunction[A, F[B]])(implicit F: Applicative[F]) =
new Service((a: A) => pf.applyOrElse(a, Function.const(F.pure(null.asInstanceOf[B]))))
implicit def monad[F[_], A](implicit F: Monad[F]) =
new Monad[Service[F, A, *]] {
def pure[B](b: B): Service[F, A, B] =
Service.pure[F, A, B](b)
def flatMap[B, C](fb: Service[F, A, B])(f: B => Service[F, A, C]) =
fb.flatMap(f)
def tailRecM[B, C](b: B)(f: B => Service[F, A, Either[B, C]]) =
???
}
implicit def monoidK[F[_], A](implicit F: Monad[F], FK: MonoidK[F]) =
new MonoidK[Service[F, A, *]] {
def combineK[B](x: Service[F, A, B], y: Service[F, A, B]) =
new Service(a => F.flatMap(x.run(a)) {
case null => y.run(a)
case b => F.pure(b)
})
def empty[B] =
new Service(Function.const(FK.empty))
}
// TODO only needs Applicative
implicit def local[F[_], A](implicit F: Monad[F]): Local[Service[F, A, *], A] =
new Local[Service[F, A, *], A] {
def applicative =
monad[F, A]
def ask[A2 >: A] =
new Service(F.pure)
def local[B](fb: Service[F, A, B])(f: A => A) =
fb.local(f)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment