Skip to content

Instantly share code, notes, and snippets.

@kenbot
Last active November 25, 2015 15:51
Show Gist options
  • Save kenbot/6a5421fe040c485746f2 to your computer and use it in GitHub Desktop.
Save kenbot/6a5421fe040c485746f2 to your computer and use it in GitHub Desktop.
Garden Hoses as a Category in Scala
// Category
//
trait Category[Arrow[_,_]] {
def compose[A,B,C](c1: Arrow[B,C], c2: Arrow[A,B]): Arrow[A,C]
def id[A]: Arrow[A,A]
}
object Category {
implicit object FunctionCat extends Category[Function1] {
def compose[A,B,C](f: B => C, g: A => B): A => C = f compose g
def id[A]: A => A = identity
}
}
// Connection types
// In order of water flow
trait WaterSupply
trait TapScrew
trait FemaleMale
trait MaleHose
trait HoseFemale
trait Exit
sealed trait Hose[In, Out] {
def leaks: Int
def kinked: Boolean
final def <<[A](in: Hose[A, In]): Hose[A, Out] =
in >> this
final def >>[A](out: Hose[Out, A]): Hose[In, A] = (this, out) match {
case (_, _) => new >>[In, Out, A](this, out)
}
}
// Fills in some fields for hard gadgety hose components
trait HoseGadget[In, Out] extends Hose[In, Out] {
def leaky: Boolean
override final def kinked = false
override final def leaks: Int = if (leaky) 1 else 0
}
case class Tap(leaky: Boolean) extends HoseGadget[WaterSupply, TapScrew]
case class TapNozzle(leaky: Boolean) extends HoseGadget[TapScrew, FemaleMale]
case class MaleNozzle(leaky: Boolean) extends HoseGadget[FemaleMale, MaleHose]
case class HosePipe(leaks: Int, kinked: Boolean) extends Hose[MaleHose, HoseFemale]
case class FemaleNozzle(leaky: Boolean) extends HoseGadget[HoseFemale, FemaleMale]
case class SprayGun(leaky: Boolean) extends HoseGadget[FemaleMale, Exit]
case class Sprinkler(leaky: Boolean) extends HoseGadget[FemaleMale, Exit]
// Identity hose
case class EmptyHose[A] extends Hose[A, A] {
def leaks = 0
def kinked = false
}
// Composition of two hoses
case class >>[In, Mid, Out](
in: Hose[In, Mid],
out: Hose[Mid, Out]) extends Hose[In, Out] {
def kinked = in.kinked || out.kinked
def leaks = in.leaks + out.leaks
override def toString: String = s"$in >> $out"
}
object Hose {
implicit object HoseCategory extends Category[Hose] {
def compose[A,B,C](c1: Hose[B,C], c2: Hose[A,B]): Hose[A,C] =
c1 << c2
def id[A]: Hose[A,A] = EmptyHose[A]
}
def empty[A]: Hose[A,A] = EmptyHose[A]
def newHose: Hose[FemaleMale, FemaleMale] =
MaleNozzle(false) >> HosePipe(0, false) >> FemaleNozzle(false)
def oldHose(leaks: Int, kinked: Boolean) =
MaleNozzle(false) >> HosePipe(leaks, kinked) >> FemaleNozzle(false)
def newSprayGun = SprayGun(false)
def newSprinkler = Sprinkler(false)
def newFittedTap = Tap(false) >> TapNozzle(false)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment