Profunctorial

Data

by Oleg Nizhnikov

from

Trees

(1 + x) < 4 AND y == 6

Syntax Trees

AND(
   LESS(
     PLUS(
       LITERAL(1),
       VARIABLE(x)
     )
   ),
   EQUALS(
    VARIABLE(Y),
    LITERAL(6)
   )
)
enum Lang:
  case Universe(options: TypeOptions)

  case Record(name: RecordKey, typ: Lang, options: TypeOptions)
  case Extend(base: Lang, deps: Lang)

  case Function(domain: Lang, effect: Lang, body: Lang)
  case Builtin(bt: BuiltinType)

  case GetKey(key: RecordKey, up: Int)
  case Unit
  case Id
  case Set(key: RecordKey, term: Lang)
  case Merge(base: Lang, deps: Lang)

  case Narrow(term: Lang, typ: Lang)

  case AndThen(left: Lang, right: Lang)

  case Capture(domain: Lang, body: Lang)
  case Apply

  case External(ref: LibRef)
  case Str(value: String)
  case Float(value: Double)
  case Integer(value: BigInt)
  case Bool(value: Boolean)

Abstract Syntax Trees

 Modularity

Order

Database

Customer

Product

Cart

RPC Client

Message Broker

Network

Concurrent Framework

Equivalence

Equivalence

B

A

to

from

to(from(b)) = b

from(to(a)) = a

Equivalence

Vector[A]

List[A]

.toVector

.toList

v.toList.toVector == v

l.toVector.toList == l

Equivalence

Boolean => A

(A, A)

fromTuple

toPair

def fromPair[A](p: (A, A)) = (b: Boolean) => if (b) p._1 else p._2

def toPair[A](f: Boolean => A) = (f(true), f(false))

Equivalence

() => A

A

capture

eval

def capture[A](a: A) = () => a

def eval[A](t: () => A) = t()

Equivalence

A

toLazy

fromLazy

def toLazy[A](a: A): Lazy[A] = new:
  def use[B](f: A => B) = f(a)
  
def fromLazy[A](l: Lazy[A]): A = l.use(identity)
  
trait Lazy[A]:
  def use[B](f: A => B): B

Representation

trait Representable[F[_]] extends Functor[F]:
  type Representation

  def index[A](f: F[A]): Representation => A

  def tabulate[A](f: Representation => A): F[A]
end Representable

Representable Functor

given [R]: Representable[[a] =>> R => a] with
  type Representation = R

  def index[A](fa: R => A): Representation => A = fa

  def tabulate[A](f: R => A): R => A = f

Representable Function

given [R]: Representable[[a] =>> (a, a)] with
  type Representation = Boolean

  def index[A](fa: (A, A)): Boolean => A = if _ then fa._1 else fa._2

  def tabulate[A](f: Boolean => A): (A, A) = (f(true), f(false))

Representable Tuple

enum User:
  case Anonymous
  case Authorized(name: String, email: String)
  case Admin(key: Seq[Byte])

Data

trait FromUser[R]:
  def anonymous: R
  def authorized(name: String, email: String): R
  def admin(key: Seq[Byte]): R
enum User:
  case Anonymous
  case Authorized(name: String, email: String)
  case Admin(key: Seq[Byte])

Representable Data

trait FromUser[R]:
  def anonymous: R
  def authorized(name: String, email: String): R
  def admin(key: Seq[Byte]): R
  
 type User = Rep[FromUser]
given Representable[FromUser] with
  type Representation = User

  def index[A](fa: FromUser[A]): User => A = _ match
    case User.Anonymous               => fa.anonymous
    case User.Authorized(name, email) => fa.authorized(name, email)
    case User.Admin(key)              => fa.admin(key)

  def tabulate[A](f: User => A): FromUser[A] = new:
    def anonymous                               = f(User.Anonymous)
    def authorized(name: String, email: String) = f(User.Authorized(name, email))
    def admin(key: Seq[Byte])                   = f(User.Admin(key))

Representable Data

Repr

trait Lazy[A]:
  def use[B](f: A => B): B
trait Repr[F[_]]:
  def apply[B](fb: F[B]): B

A => B

F[B]

Repr[F]

A

trait Representable[F[_]] extends Functor[F]:
  def tabulate[A](f: Repr[F] => A): F[A]

Representable Functor

def index[F[_], A](f: F[A]): Repr[F] => A = _(f)

Representable Data

enum User:
  case Anonymous
  case Authorized(name: String, email: String)
  case Admin(key: Seq[Byte])
trait FromUser[R]:
  def anonymous: R
  def authorized(name: String, email: String): R
  def admin(key: Seq[Byte]): R

Repr[FromUser]  ~   User

trait FromUser[R]:
  def anonymous: R
  def authorized(name: String, email: String, friends: List[R]): R
  def admin(key: Seq[Byte]): R

Non-representable non-functor

enum User:
  case Anonymous
  case Authorized(name: String, email: String, friends: List[User])
  case Admin(key: Seq[Byte])

Repr[FromUser]  ~   User

Non-representable non-functor

def toRepr(user: User): Repr[FromUser] = user match
  case User.Anonymous                        =>
    new { def apply[B](f: FromUser[B]) = f.anonymous }
  case User.Admin(key)                       =>
    new { def apply[B](f: FromUser[B]) = f.admin(key) }
  case User.Authorized(name, email, friends) =>
    new { def apply[B](f: FromUser[B]) = f.authorized(name, email, friends.map(toRepr(_)(f))) }
def fromRepr(repr: Repr[FromUser]): User = repr(new FromUser[User] {
  def anonymous                                                          =
    User.Anonymous
  def admin(key: Seq[Byte])                                              =
    User.Admin(key)
  def authorized(name: String, email: String, friends: List[User]): User =
    User.Authorized(name, email, friends)
})
  • Church 

  • Boehm-Berarducci 

  • Tagless-final

Encoding names

enum Bool:
  case False, True
trait FromBool[A]:
  def False: A
  def True: A
  • Church 

  • Boehm-Berarducci 

  • Tagless-final

Encoding names

enum Nat:
  case Zero
  case Suc(prev: Nat)
trait FromNat[A]:
  def Zero: A
  def Suc(prev: A): A
  • Church 

  • Boehm-Berarducci 

  • Tagless-final

Encoding names

enum List[A]:
  case Nil
  case Cons(head: A, tail: List[A])
trait FromList[A, B]:
  def Nil: B
  def Cons(head: A, tail: B): B
  • Church 

  • Boehm-Berarducci 

  • Tagless-final

Encoding names

enum User:
  case Anonymous
  case Authorized(
    name: String, 
    email: String, 
    friends: List[User]
    )
  case Admin(key: Seq[Byte])
trait FromUser[R]:
  def anonymous: R
  def authorized(
      name: String, 
      email: String, 
      friends: List[R]
      ): R
  def admin(key: Seq[Byte]): R

Contravariance

trait Repr[F[_]]:
  def apply[B](fb: F[B]): B

Contravariance

trait Repr[-F[_]]:
  def apply[B](fb: F[B]): B

Contravariance

trait OfNotFound[R]:
  def notFound(id: String): R
case class NotFound(id: String)
trait OfAlreadyExists[R]:
  def alreadyExists(id: String): R
case class AlreadyExists(id String)
trait `OfNotFound & OfAlreadyExits`[R]:
  def notFound(id: String): R
  def alreadyExists(id: String): R
enum `NotFound | AlreadyExists`:
  case NotFound(id: String)
  case AlreadyExists(id: String)

for non-recursive F[_] and G[_]

Repr[F & G] = Repr[F] | Repr[G]

Intersection

trait OfBools[A]:
  def True: A
  def False: A
  def Not(x: A): A
  def And(x: A, y: A): A
  def Or(x: A, y: A): A
enum Bools:
  case True
  case False
  case Not(x: Bools)
  case And(x: Bools, y: Bools)
  case Or(x: Bools, y: Bools)
trait OfNumbers[A]:
  def fromInt(x: Int): A
  def plus(x: A, y: A): A
  def multiply(x: A, y: A): A
enum Numbers:
  case FromInt(x: Int)
  case Plus(x: Numbers, y: Numbers)
  case Multiply(x: Numbers, y: Numbers)
trait OfComparison[A]:
  def less(x: A, y: A): A
  def equals(x: A, y: A): A
enum Comparison:
  case Less(x: Comparison, y: Comparison)
  case Equals(x: Comparison, y: Comparison)

Intersection

trait `OfBools & OfNumbers & OfComparison`[A]:
  def True: A
  def False: A
  def Not(x: A): A
  def And(x: A, y: A): A
  def Or(x: A, y: A): A

  def fromInt(x: Int): A
  def plus(x: Int, y: Int): A
  def multiply(x: Int, y: Int): A

  def less(x: A, y: A): A
  def equals(x: A, y: A): A

Extensibility

enum Expr:
  case True
  case False
  case Not(x: Expr)
  case And(x: Expr, y: Expr)
  case Or(x: Expr, y: Expr)
  
  case FromInt(x: Int)
  case Plus(x: Expr, y: Expr)
  case Multiply(x: Expr, y: Expr)
  
  case Less(x: Expr, y: Expr)
  case Equals(x: Expr, y: Expr)
val expr: Repr[OfNumbers && OfComparison] = new {
  def apply[A](L: OfNumbers[A] & OfComparison[A]) =
    L.equals(
      L.fromInt(4),
      L.plus(
        L.fromInt(1),
        L.fromInt(3)
      )
    )
}
<expr xmlns:n="lambda::days::numbers"
      xmlns:c="lambda::days::comparison">
    <c:equals>
        <n:fromInt>4</n:fromInt>
        <n:plus>
            <n:fromInt>1</n:fromInt>
            <n:fromInt>3</n:fromInt>
        </n:plus>
    </c:equals>
</expr>

Extensibility

val expr1: Repr[OfNumbers && OfComparison && OfBools] = expr

Construction

trait FromNat[A]:
  def Zero: A
  def Suc(prev: A): A

object FromNat extends FromNat[Repr[FromNat]]:
  def Zero: Repr[FromNat]                     =
    new { def apply[A](f: FromNat[A]) = f.Zero }
  def Suc(prev: Repr[FromNat]): Repr[FromNat] =
    new { def apply[A](f: FromNat[A]) = f.Suc(prev(f)) }

Matching Problem

trait FromNat[A]:
  def Zero: A
  def Suc(prev: A): A

def prev(n: Repr[FromNat]): Repr[FromNat] = ???

Matching Problem

 

trait FromNat[A]:
  def Zero: A
  def Suc(prev: A): A

def prev(n: Repr[FromNat]): Repr[FromNat] =
  n(new FromNat[(Repr[FromNat], Repr[FromNat])] {
    def Zero: (Repr[FromNat], Repr[FromNat]) =
      (FromNat.Zero, FromNat.Zero)

    def Suc(prev: (Repr[FromNat], Repr[FromNat])) =
      (prev._2, FromNat.Suc(prev._1))
  })._1

Recursion

Fixpoint

final case class Fix[F[_]](value: F[Fix[F]]):
  def fold[A](f: F[A] => A)(using Functor[F]): A = f(value.map(_.fold(f)))

  def foldEval[A](f: F[A] => Eval[A])(using Traverse[F]): Eval[A] =
    value.traverse(_.foldEval(f)).flatMap(f)

  def foldTail[A](f: F[A] => A)(using Traverse[F]): A = 
    foldEval[A](fx => Eval.later(f(fx))).value
    
    
object Fix:
  def unfold[F[_]: Functor, A](init: A)(f: A => F[A]): Fix[F] =
    Fix(f(init).map(Fix.unfold(_)(f)))

  def unfoldEval[F[_]: Traverse, A](init: A)(f: A => Eval[F[A]]): Eval[Fix[F]] =
    f(init).flatMap(_.traverse(Fix.unfoldEval(_)(f))).map(Fix(_)).defer

  def unfoldTail[F[_]: Traverse, A](init: A)(f: A => F[A]): Fix[F] =
    unfoldEval(init)(a => Eval.later(f(a))).value

Fixpoint

final case class Fix[+F[+_]](value: F[Fix[F]]):
  def fold[A, F1[x] >: F[x]: Functor](f: F1[A] => A): A = f(value.map(_.fold(f)))

  def foldEval[A, F1[x] >: F[x]: Traverse](f: F1[A] => Eval[A]): Eval[A] =
    value.traverse(_.foldEval(f)).flatMap(f).defer

  def foldTail[A, F1[x] >: F[x]: Traverse](f: F1[A] => A): A =
    foldEval[A, F1](fx => Eval.later(f(fx))).value


object Fix:
  def unfold[F[+_]: Functor, A](init: A)(f: A => F[A]): Fix[F] =
    Fix(f(init).map(Fix.unfold(_)(f)))

  def unfoldEval[F[+_]: Traverse, A](init: A)(f: A => Eval[F[A]]): Eval[Fix[F]] =
    f(init).flatMap(_.traverse(Fix.unfoldEval(_)(f))).map(Fix(_)).defer

  def unfoldTail[F[+_]: Traverse, A](init: A)(f: A => F[A]): Fix[F] =
    unfoldEval(init)(a => Eval.later(f(a))).value

Tree Nodes

enum Bools[+A] derives Traverse:
  case True
  case False
  case Not(x: A)
  case And(x: A, y: A)
  case Or(x: A, y: A)
enum Numbers[+A] derives Traverse:
  case FromInt(x: Int)
  case Plus(x: A, y: A)
  case Multiply(x: A, y: A)
enum Comparison[+A] derives Traverse:
  case Less(x: A, y: A)
  case Equals(x: A, y: A)

Fixpoint

val expr: Fix[Numbers || Comparison] =
  Fix(Comparison.Equals(
      Fix(Numbers.FromInt(4)),
      Fix(Numbers.Plus(
        Fix(Numbers.FromInt(1)),
        Fix(Numbers.FromInt(3))
      ))
    ))
    
val expr1: Fix[Numbers || Comparison || Bools] = expr

Instances

summon[Traverse[Numbers || Comparison || Bools]](using ???)

Handling

def handing(x: Int): Int =
  try {
    2 / x + handing(x - 1)
  } catch {
    case _: ArithmeticException => 0
  }

Handling

def handing(x: Int): Int =
  try {
    2 / x + handing(x - 1)
  } catch {
    case _: ArithmeticException => 0
  }
 
 
 
 
def handle[F[+_], H[+_]](
    fix: Fix[F || H]
)(
    handler: H[Fix[F || H]] => Fix[F]
): Fix[F]

Handling

def handle[F[+_], H[+_]](
    fix: Fix[F || H]
)(
    handler: H[Fix[F || H]] => Fix[F]
)(using
    Typeable[H[Fix[F || H]]],
    Typeable[F[Fix[F || H]]],
    Functor[F]
): Fix[F] =
  fix.value match
    case h: H[Fix[F || H]] => handler(h)
    case f: F[Fix[F || H]] => Fix(f.map(handle(_)(handler)))

Profunctorial

In

trait FromUser[R]:
  def anonymous: R
  def authorized(name: String, email: String, friends: Vector[R]): R
  def admin(key: Seq[Byte]): R

Variant

Di

trait FromUser[-I, +O]:
  def anonymous: O
  def authorized(name: String, email: String, friends: Vector[I]): O
  def admin(key: Seq[Byte]): O

Variant

enum UserF[+A]:
  case Anonymous
  case Authorized(name: String, email: String, friends: Vector[A])
  case Admin(key: Seq[Byte])

FromUser[I, O]

UserF[I] => O

Layer

trait Layer[-P[-_, +_]]:
  def unwrap[A](f: P[Layer[P], A]): A
trait Wrapped[+F[+_]]:
  def patMat[A](f: F[Wrapped[F]] => A): A

~

F[Wrapped[F]]

~

~

Fix[F]

Layer

Layer[FromUser]
trait WrappedUser:
  def patMat[A](f: UserF[WrappedUser] => A): A

~

UserF[WrappedUser]

~

trait FromUser[-I, +O]:
  def anonymous: O
  def authorized(
      name: String,
      email: String,
      friends: Vector[I]
  ): O
  def admin(key: Seq[Byte]): O
enum UserF[+R]:
  case Anonymous
  case Authorized(
      name: String,
      email: String,
      friends: Vector[R]
  )
  case Admin(key: Seq[Byte])
Fix[UserF]

~

Instances

for some F[_]

  • P[A, B] is equivalent to F[A] => B 

  • F[_] is Functor

  • F[_] is Traversable

Instances

P[A, B] is equivalent to F[A] => B 

trait ProRepresentable[P[_, _]]:
  type F[_]
  def tabulate[A, B](f: F[A] => B): P[A, B]
  def index[A, B](p: P[A, B])(fa: F[A]): B

Instances

P[A, B] is equivalent to F[A] => B 

trait ProRep[P[_, _], B]:
  def apply[A](p: P[A, B]): B

trait ProRepresentable[P[_, _]]:
  def tabulate[A, B](f: ProRep[P, A] => B): P[A, B]

Instances

F[_] is a Functor

trait ProLeftMap[P[_, _]]:
  def leftMap[A, B, L](pab: P[A, B])(f: L => A): P[L, B]

Instances

F[_] is Traversable

trait ProSequence[P[_, _]]:
  def sequence[A, B, F[_]: Applicative](pab: P[A, B]): P[F[A], F[B]]
 
def sequence[A, B, F[_]: Applicative](f: Base[A] => B): Base[F[A]] => F[B]

  P[A, B]

 Base[A] => B

Instances

trait ProTraverseRep[P[_, _]] 
  extends ProRepresentable[P] with ProLeftMap[P] with ProSequence[P]:
  def proTraverse[L, A, B, F[_]: Applicative](
      fromRep: ProRep[P, A] => B,
      f: L => A
  ): P[F[L], F[B]]
  • P[A, B] is equivalent to ProRep[P, A] => B 

  • ProRep[P, _] is Functor

  • ProRep[P, _] is Traversable

Instances

trait ProTraverseRep[P[_, _]] 
	extends ProRepresentable[P] with ProLeftMap[P] with ProSequence[P]:
  def proTraverse[L, A, B, F[_]](
      fromRep: ProRep[P, A] => B,
      f: L => A
  ): P[F[L], F[B]]
  
  override def leftMap[A, B, L](pab: P[A, B])(f: L => A): P[L, B] =
    proTraverse[L, A, B, [x] =>> x](_(pab), f)

  override def sequence[A, B, F[_]: Applicative](pab: P[A, B]): P[F[A], F[B]] =
    proTraverse[A, A, B, F](_(pab), identity)

  override def tabulate[A, B](f: ProRep[P, A] => B): P[A, B] =
    proTraverse[A, A, B, [x] =>> x](f, identity)

Generalized Recursion

trait Layer[-P[-_, +_]]:
  def unwrap[A](f: P[Layer[P], A]): A

  def fold[A, P1[x, y] <: P[x, y]](f: P1[A, A])(using ProLeftMap[P1]): A =
    unwrap(f.lmap[Layer[P]](_.fold(f)))

  def foldEval[A, P1[x, y] <: P[x, y]](f: P1[Eval[A], Eval[A]])(using ProLeftMap[P1]): Eval[A] =
    unwrap(f.lmap(_.foldEval(f))).defer

  def foldTail[A, P1[x, y] <: P[x, y]](f: P1[A, A])(using ProTraverseRep[P1]): A =
    foldEval(f.seq[Eval]).value
    
    
object Layer:
  def unfold[A, P[-_, +_]](ca: Coalgebra[P, A])(init: A)(using ProLeftMap[P]): Layer[P] =
    new:
      def unwrap[X](f: P[Layer[P], X]): X =
        ca(init, f.lmap[A](Layer.unfold(ca)))
        
  def apply[P[-_, +_], Q[-i, +o] <: P[i, o]](using P: ProTraverseRep[P]): P[Layer[Q], Layer[Q]] =
    P.tabulate(rep => new { def unwrap[A](pla: Q[Layer[Q], A]) = rep(pla) })

Extensibility

trait Bools[-I, +O] 
  derives ProTraverseRep:
  def True: O
  def False: O
  def Not(x: I): O
  def And(x: I, y: I): O
  def Or(x: I, y: I): O
trait Numbers[-I, +O] 
  derives ProTraverseRep:
  def fromInt(x: Int): O
  def plus(x: I, y: I): O
  def multiply(x: I, y: I): O
trait Comparison[-I, +O] 
  derives ProTraverseRep:
  def less(x: I, y: I): O
  def equals(x: I, y: I): O

Extensibility

type L[-i, +o] = (Numbers &&& Comparison)[i, o]

val expr: Layer[L] =
  Layer[Comparison, L].equals(
    Layer[Numbers, L].fromInt(4),
    Layer[Numbers, L].plus(
      Layer[Numbers, L].fromInt(1),
      Layer[Numbers, L].fromInt(3)
    )
  )

type L2[-i, +o] = (Numbers &&& Comparison &&& Bools)[i, o]

val expr1: Layer[L2] = expr

Open Questions

  • How to combine traverse instances
  • How to partially handle effects
  • What to do with GADT

Profunctorial Effects

Script of Types

case class Dictionary(selectDynamic: Map[String, Any]) extends Selectable:
  def as[T]: Dictionary & T = this.asInstanceOf[Dictionary & T]

type Person = {
  val name: String
  val age: Int
}

val x = Dictionary(Map("age" -> 87, "name" -> "Aragorn")).as[Person]

println(x.age: Int)
println(x.name: String)

Script of Mergeable Types

case class Dictionary(selectDynamic: Map[String, Any]) extends Selectable:
  type Underlying
  def as[T]: Dictionary & T { type Underlying <: T } = 
  	this.asInstanceOf[Dictionary & T & { type Underlying <: T }]

  def merge[Q](other: Dictionary & Q) =
    Dictionary(selectDynamic ++ other.selectDynamic).as[Underlying & Q]

Script of Mergeable Types


type Person = {
  val name: String
  val age: Int
}

val x = Dictionary(Map("age" -> 87, "name" -> "Aragorn")).as[Person]

type Friendly = {
  val friends: List[String]
}

val y = Dictionary(Map("friends" -> List("Frodo", "Legolas", "Gimli"))).as[Friendly]

val z = x merge y

println(z.age: Int)
println(z.name: String)
println(z.friends: List[String])

Extensibility

case class Pro[P[-_, +_], -I, +O](p: P[I, O], instance: ProTraverseRep[P])

case class Multi[-I, +O](pros: Map[String, Pro[?, I, O]]) extends Selectable:
  def selectDynamic(name: String): Pro[?, I, O] = pros(name)
  
  
type &&&[P[-_, +_], Q[-_, +_]] = [a, b] =>> P[a, b] & Q[a, b]

Extensibility

trait OfBool[-I, +O] 
  derives ProTraverseRep:
  def True: O
  def False: O
  def Not(x: I): O
  def And(x: I, y: I): O
  def Or(x: I, y: I): O

type Bools[-I, +O] =
  Multi[I, O] & {
    val bools: Pro[OfBool, I, O]
  }
trait OfNumber[-I, +O]
  derives ProTraverseRep:
  def fromInt(x: Int): O
  def plus(x: I, y: I): O
  def multiply(x: I, y: I): O



type Numbers[-I, +O] =
  Multi[I, O] & {
    val numbers: Pro[OfNumber, I, O]
  }
trait OfCompare[-I, +O]
  derives ProTraverseRep:
  def less(x: I, y: I): O
  def equals(x: I, y: I): O




type Compare[-I, +O] =
  Multi[I, O] & {
    val comparison: OfCompare[I, O]
  }

Profunctorial Effects

case class Pro[P[-_, +_], -I, +O](p: P[I, O], instance: ProTraverseRep[P])

case class Multi[-I, +O](pros: Map[String, Pro[?, I, O]]) extends Selectable:
  def selectDynamic(name: String): Pro[?, I, O] = pros(name)
  
type &&&[P[-_, +_], Q[-_, +_]] = [a, b] =>> P[a, b] & Q[a, b]

Profunctorial Effects

case class Pro[P[-_, +_], -I, +O](p: P[I, O], instance: ProTraverseRep[P])

case class Multi[-I, +O](pros: Map[String, Pro[?, I, O]]) extends Selectable:
  def selectDynamic(name: String): Pro[?, I, O] = pros(name)
  
type &&&[P[-_, +_], Q[-_, +_]] = [a, b] =>> P[a, b] & Q[a, b]
  
trait Layer[-P[-_, +_]]:
  def unwrap[O](p: P[ProData[P], O]): O

Profunctorial Effects

case class Pro[P[-_, +_], -I, +O](p: P[I, O], instance: ProTraverseRep[P])

case class Multi[-I, +O](pros: Map[String, Pro[?, I, O]]) extends Selectable:
  def selectDynamic(name: String): Pro[?, I, O] = pros(name)
  
type &&&[P[-_, +_], Q[-_, +_]] = [a, b] =>> P[a, b] & Q[a, b]

trait Layer[-P[-_, +_]]:
  def unwrap[O](p: P[ProData[P], O]): O

trait Handler[From[-_, +_], -To[-_, +_]]:
  def handle[P[-i, +o]]: From[ProData[P &&& From], ProData[P &&& To]]

Profunctorial Effects

case class Pro[P[-_, +_], -I, +O](p: P[I, O], instance: ProTraverseRep[P])

case class Multi[-I, +O](pros: Map[String, Pro[?, I, O]]) extends Selectable:
  def selectDynamic(name: String): Pro[?, I, O] = pros(name)
  
type &&&[P[-_, +_], Q[-_, +_]] = [a, b] =>> P[a, b] & Q[a, b]
  
trait Layer[-P[-_, +_]]:
  def unwrap[O](p: P[ProData[P], O]): O

trait Handler[From[-_, +_], -To[-_, +_]]:
  def handle[P[-i, +o]]: From[ProData[P &&& From], ProData[P &&& To]]

enum ProData[-P[-_, +_]]:
  case Construct(layer: Layer[P])
  case Eliminate[-P[-_, +_], Q[-_, +_]](
      data: ProData[P &&& Q],
      handler: Handler[Q, P]
  ) extends ProData[P]
  

Profunctorial Handlers

trait Handler[From[-_, +_], -To[-_, +_]]:
  def handle[P[-i, +o]]: From[ProData[P &&& From], ProData[P &&& To]]
  
  
object Handler:
  def recursive[From[-i, +o], To[-_, +_]](handler: Handler[From, From &&& To]): Handler[From, To]

Elimination

Order

Customer

Product

Cart

Elimination

Order

Database

Customer

Product

Cart

RPC Client

Message Broker

Elimination

Order

Database

Customer

Product

Cart

RPC Client

Message Broker

Network

Concurrent Framework

Elimination

Order

Database

Customer

Product

Cart

RPC Client

Message Broker

Network

Concurrent Framework

Elimination

Network

Concurrent Framework

Higher Kinds

trait Repr[Alg[f[_]], A]:
  def apply[F[_]](alg: Alg[F]): F[A]

trait Bools[F[_]]:
  def True: F[Boolean]
  def False: F[Boolean]
  def Not(x: F[Boolean]): F[Boolean]
  def And(x: F[Boolean], y: F[Boolean]): F[Boolean]
  def Or(x: F[Boolean], y: F[Boolean]): F[Boolean]

trait Numbers[F[_]]:
  def fromInt(x: Int): F[Int]
  def plus(x: F[Int], y: F[Int]): F[Int]
  def multiply(x: F[Int], y: F[Int]): F[Int]

trait Comparison[F[_]]:
  def less(x: F[Int], y: F[Int]): F[Boolean]
  def equals(x: F[Int], y: F[Int]): F[Boolean]

Indexed Church Encoding

trait TraverseHK[Alg[f[_], _]]:
  def traverse[A, F[_], X[_], Y[_]](alg: Alg[X, A])(f: [a] => X[a] => F[Y[a]]): Alg[Y, A]

case class Fix[+Alg[+_[_], _], R](alg: Alg[Fix[Alg, *], R])

enum Bools[+F[_], A] derives TraverseHK:
  case True                                    extends Bools[Nothing, Boolean]
  case False                                   extends Bools[Nothing, Boolean]
  case Not[F[_]](x: F[Boolean])                extends Bools[F, Boolean]
  case And[F[_]](x: F[Boolean], y: F[Boolean]) extends Bools[F, Boolean]
  case Or[F[_]](x: F[Boolean], y: F[Boolean])  extends Bools[F, Boolean]

enum Numbers[+F[_], A] derives TraverseHK:
  case FromInt(x: Int)                extends Numbers[Nothing, Int]
  case Plus(x: F[Int], y: F[Int])     extends Numbers[Nothing, Int]
  case Multiply(x: F[Int], y: F[Int]) extends Numbers[Nothing, Int]

enum Comparison[+F[_], A] derives TraverseHK:
  case Less(x: F[Int], y: F[Int])   extends Comparison[F, Boolean]
  case Equals(x: F[Int], y: F[Int]) extends Comparison[F, Boolean]

Indexed Initial Fixpoint

trait Bools[-I[_], +O[_]]:
  def True: O[Boolean]
  def False: O[Boolean]
  def Not(x: I[Boolean]): O[Boolean]
  def And(x: I[Boolean], y: I[Boolean]): O[Boolean]
  def Or(x: I[Boolean], y: I[Boolean]): O[Boolean]

trait Numbers[-I[_], +O[_]]:
  def fromInt(x: Int): O[Int]
  def plus(x: I[Int], y: I[Int]): O[Int]
  def multiply(x: I[Int], y: I[Int]): O[Int]

trait Comparison[-I[_], +O[_]]:
  def less(x: I[Int], y: I[Int]): O[Boolean]
  def equals(x: I[Int], y: I[Int]): O[Boolean]

Indexed Profunctorial Modules

trait ProRepHK[P[_[_], _[_]], I[_], A]:
  def apply[O[_]](p: P[I, O]): O[A]

trait ProRepresentableHK[P[_[_], _[_]]]:
  def tabulate[I[_], O[_]](f: [A] => ProRepHK[P, I, A] => O[A]): P[I, O]

trait ProLeftMapHK[P[_[_], _[_]]]:
  def leftMap[I[_], O[_], L[_]](pab: P[I, O])(f: [A] => L[A] => I[A]): P[L, O]


trait ProSequenceHK[P[_[_], _[_]]]:
  def sequence[I[_], O[_], F[_]: Applicative](pab: P[I, O]): P[[A] =>> F[I[A]], [A] =>> F[O[A]]]

trait ProTraverseRepHK[P[_[_], _[_]]] 
  extends ProRepresentableHK[P] with ProLeftMapHK[P] with ProSequenceHK[P]:
  def proTraverse[L[_], I[_], O[_], F[_]: Applicative](
      fromRep: [A] => ProRepHK[P, I, A] => O[A],
      f: [A] => L[A] => I[A]
  ): P[[A] =>> F[L[A]], [A] =>> F[O[A]]]

Indexed Profunctorial Typeclasses

trait LayerHK[-P[-_[_], +_[_]], A]:
  def unwrap[O[_]](f: P[LayerHK[P, *], O]): O[A]

  def fold[R[_], P1[i[_], o[_]] <: P[i, o]](f: P1[R, R])(using ProLeftMapHK[P1]): R[A] =
    unwrap(f.lmap[LayerHK[P, *]]([A] => (l: LayerHK[P, A]) => l.fold(f)))

  def foldEval[R[_], P1[i[_], o[_]] <: P[i, o]](f: P1[[A] =>> Eval[R[A]], [A] =>> Eval[R[A]]])(using
      ProLeftMapHK[P1]
  ): Eval[R[A]] =
    unwrap(f.lmap([A] => (l: LayerHK[P, A]) => l.foldEval(f))).defer

  def foldTail[R[_], P1[i[_], o[_]] <: P[i, o]](f: P1[R, R])(using ProTraverseRepHK[P1]): R[A] =
    foldEval(f.seq[Eval]).value
end LayerHK

trait CoalgebraHK[P[-_[_], +_[_]], I[_]]:
  def apply[O[_], A](a: I[A], p: P[I, O]): O[A]

object LayerHK:
  def unfold[I[_], A, P[-_[_], +_[_]]](ca: CoalgebraHK[P, I])(init: I[A])(using
      ProLeftMapHK[P]
  ): LayerHK[P, A] =
    new:
      def unwrap[O[_]](f: P[LayerHK[P, *], O]): O[A] =
        ca(init, f.lmap[I]([B] => (ia: I[B]) => LayerHK.unfold(ca)(ia)))

Indexed Profunctorial Encoding

Thank you!

Question time!

telegram: @odomontois

twitter: @odomontois

profdata

By Oleg Nizhnik

profdata

  • 1,042