Skip to content

Instantly share code, notes, and snippets.

@chrislewis
Created February 9, 2021 20:12
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 chrislewis/a0298bbae8f1842bf0f0652766fa030d to your computer and use it in GitHub Desktop.
Save chrislewis/a0298bbae8f1842bf0f0652766fa030d to your computer and use it in GitHub Desktop.
An encoding of typesafe and dynamic composition of a finite set of behaviors
import scala.language.implicitConversions
/**
* A hypothetical type that may possess any combination of several properties, each encoded as a trait specifying
* that property. Our example type may have any of these properties:
*
* links: Set[String]
* meta: Map[String, String]
* other: Int
* another: String
*/
sealed trait Rel
sealed trait WithLinks extends Rel {
def links: Set[String]
}
sealed trait WithMeta extends Rel {
def meta: Map[String, String]
}
sealed trait WithOther extends Rel {
def other: Int
}
sealed trait WithAnother extends Rel {
def another: String
}
/**
* A simple container type to hold a value, which itself may be a property or a property composed with
* another container.
*/
trait One[A] {
def value: A
}
object One {
/** Us a container of A as an A. */
implicit def oneToA[A](one: One[A]): A = one.value
/** Us a container of a container of A as an A. */
implicit def oneToA2[A, R](one: One[One[A] with R]): A = one.value.value
/** Us a container of a container of a container of A as an A. */
implicit def oneToA3[A, R, RR](one: One[One[One[A] with RR] with R]): A = one.value.value.value
}
/**
* Builder functions to construct various compositions of behavior.
*
* val wl = Rel.withLinks(Set("fake"))
* val wlm = Rel.withMeta(wl, Map("one" -> "1"))
* val wlmo = Rel.withOther(wlm, 1)
* val wlmoa = Rel.withAnother(wlmo, "hi")
*/
object Rel {
def withLinks(_links: Set[String]): WithLinks = new WithLinks {
override def links: Set[String] = _links
}
def withMeta[R <: Rel](rel: R, _meta: Map[String, String]): One[R] with WithMeta = new One[R] with WithMeta {
override def value: R = rel
override def meta: Map[String, String] = _meta
}
def withOther[R <: Rel](rel: R, _other: Int): One[R] with WithOther = new One[R] with WithOther {
override def value: R = rel
override def other: Int = _other
}
def withAnother[R <: Rel](rel: R, _another: String): One[R] with WithAnother = new One[R] with WithAnother {
override def value: R = rel
override def another: String = _another
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment