Skip to content

Instantly share code, notes, and snippets.

@johnynek
Created May 30, 2020 20:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnynek/700c69b6fd702cefa6c8df4f6f096adb to your computer and use it in GitHub Desktop.
Save johnynek/700c69b6fd702cefa6c8df4f6f096adb to your computer and use it in GitHub Desktop.
Here is an interesting pattern where you want to call an allocation function of a given size once for an entire graph, but you want to individually use alloc as you are building up the graph.
import cats._
import cats.implicits._
final case class Allocator[F[_], -T, A] private (slotCount: Int, builder: List[T] => F[A]) {
def map[B](fn: A => B)(implicit F: Functor[F]): Allocator[F, T, B] =
Allocator(slotCount, builder.andThen(_.map(fn)))
def pipeTo[T1 <: T, B](that: Allocator[F, T1, A => B])(implicit F: Applicative[F]): Allocator[F, T1, B] =
Allocator(slotCount + that.slotCount,
{ (slots) =>
val thisSlots = slots.take(slotCount)
val thatSlots = slots.drop(slotCount)
(builder(thisSlots), that.builder(thatSlots)).mapN { (a, fn) => fn(a) }
})
def product[T1 <: T, B](that: Allocator[F, T1, B])(implicit F: Applicative[F]): Allocator[F, T1, (A, B)] =
pipeTo(that.map { b => { a => (a, b) } })
def run[T1 <: T](fn: Int => F[List[T1]])(implicit F: Monad[F]): F[A] =
fn(slotCount).flatMap(builder)
}
object Allocator {
def liftF[F[_], A](a: F[A]): Allocator[F, Any, A] =
Allocator(0, { _: List[Any] => a })
def pure[F[_], A](a: A)(implicit F: Applicative[F]): Allocator[F, Any, A] = liftF(F.pure(a))
def allocSlot[F[_], A](implicit me: MonadError[F, Throwable]): Allocator[F, A, A] =
Allocator(1, { slots =>
me.catchNonFatal(slots.head)
})
implicit def applicativeAllocator[F[_]: Applicative, T]: Applicative[Allocator[F, T, *]] =
new Applicative[Allocator[F, T, *]] {
def pure[A](a: A): Allocator[F, T, A] = Allocator.pure(a)
def ap[A, B](ff: Allocator[F, T, A => B])(fa: Allocator[F, T, A]): Allocator[F, T, B] =
fa.pipeTo(ff)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment