Skip to content

Instantly share code, notes, and snippets.

@joshlemer
Last active March 11, 2018 17:26
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 joshlemer/87922f7bf8d0629f95bdb7c9dda71fe3 to your computer and use it in GitHub Desktop.
Save joshlemer/87922f7bf8d0629f95bdb7c9dda71fe3 to your computer and use it in GitHub Desktop.
package com.github.joshlemer.traitobjects
import java.io._
import com.github.joshlemer.traitobjects.traitobjects.TObj
import scala.collection.mutable.ListBuffer
import scala.language.higherKinds
import scala.language.implicitConversions
/** Wrapper to keep reference to an instance of data: D, and a typeclass instance for that data T[D] */
trait TraitObject[T[_]] {
type Data
def data: Data
def _trait: T[Data]
def withTrait[S](f: T[Data] => Data => S): S = f(_trait)(data)
}
object TraitObject {
def from[D, T[_]](_data: D, __trait: T[D]): TraitObject[T] { type Data = D} =
new TraitObject[T] {
type Data = D
val data: Data = _data
val _trait: T[Data] = __trait
}
def apply[D, T[_]](_data: D)(implicit __trait: T[D]): TraitObject[T] { type Data = D} =
new TraitObject[T] {
type Data = D
val data: Data = _data
val _trait: T[Data] = __trait
}
object Implicits {
object Converters {
implicit class DataToTraitObject[D](data: D) {
def traitObject[T[_]](implicit _trait: T[D]): TraitObject[T] { type Data = D} = apply(data)
}
}
object Conversions {
implicit def dataToTraitObject[T[_], D: T](data: D): TraitObject[T] { type Data = D} = apply(data)
implicit def traitObjectToTypeclass[T[_]](traitObject: TraitObject[T]): T[traitObject.Data] = traitObject._trait
}
}
}
trait Show[-A] {
def show(a: A): String
}
object Show {
implicit val stringShow: Show[String] = identity[String]
implicit val intShow: Show[Int] = _.toString
}
object Example extends App{
{
import TraitObject.Implicits.Conversions._
val shows: List[TraitObject[Show]] = List(1, "hello")
val strings: List[String] = shows.map(to => to._trait.show(to.data))
}
{
import TraitObject.Implicits.Converters._
val shows: List[TraitObject[Show]] = List(1.traitObject, "hello".traitObject)
val strings: List[String] = shows.map(to => to._trait.show(to.data))
}
{
// no implicits at all (except typeclass instance), just the type aliases
import traitobjects._
val shows1: List[TraitObject[Show]] = List(TraitObject(1), TraitObject("hello"))
val shows2: List[TObj[Show]] = List(TObj(1), TObj("hello"))
}
}
/** Optional shorter aliases */
package object traitobjects {
type TObj[T[_]] = TraitObject[T]
val TObj: TraitObject.type = TraitObject
}
object WritesExample extends App {
trait Writes[-T] {
def write(elem: T, message: String): Unit
def close(elem: T): Unit
}
val textFileWrites: Writes[OutputStream] = new Writes[OutputStream] {
override def write(elem: OutputStream, message: String): Unit = elem.write(s"$message\n".getBytes)
override def close(elem: OutputStream): Unit = elem.close()
}
val jsonLinesFileWrites: Writes[OutputStream] = new Writes[OutputStream] {
def asJson(str: String): String = s"""{"msg" : "$str"}"""
override def write(elem: OutputStream, message: String): Unit = elem.write(s"${asJson(message)}\n".getBytes)
override def close(elem: OutputStream): Unit = elem.close()
}
implicit val listBufferWrites: Writes[ListBuffer[String]] = new Writes[ListBuffer[String]] {
override def write(elem: ListBuffer[String], message: String): Unit = elem.append(s"$message\n")
override def close(elem: ListBuffer[String]): Unit = ()
}
implicit val printStreamWrites: Writes[PrintStream] = new Writes[PrintStream] {
override def write(elem: PrintStream, message: String): Unit = elem.append(s"$message\n")
override def close(elem: PrintStream): Unit = elem.close()
}
val inMemoryLog: ListBuffer[String] = ListBuffer[String]()
val textFile: OutputStream = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))
val jsonFile: OutputStream = new BufferedOutputStream(new FileOutputStream(new File("out.jsonl")))
val stdout: PrintStream = System.out
val stdErr: PrintStream = System.err
class CompositeLogger(loggers: TObj[Writes]*) {
def info(message: String): Unit = loggers.foreach(_.withTrait(w => w.write(_, s"[info] $message")))
def close(): Unit = loggers.foreach(_.withTrait(_.close))
}
import TObj.Implicits.Conversions._
val compositeLogger = new CompositeLogger(
inMemoryLog,
TObj.from(textFile, textFileWrites),
TObj.from(jsonFile, jsonLinesFileWrites),
stdout,
stdErr
)
compositeLogger.info("hello")
compositeLogger.info("world!")
compositeLogger.close()
}
use std::io::Write;
fn main() {
// mutable vector of unsigned bytes, which has a `Write` implementation
let mut vec: Vec<u8> = Vec::<u8>::new();
// create a mutable "trait object" from `vec`, for trait `Write`
let vec_write: &mut Write = &mut vec;
// mutable `Stderr` instance, which also has `Write` implementation
let mut stderr = std::io::stderr();
// create a mutable "trait object" from `stderr`, for trait `Write`
let stderr_write: &mut Write = &mut stderr;
// assemble bothe instances into a vector of `Write` trait objects
// (one must box them because they may have different sizes on the stack)
let writes: Vec<Box<Write>> = vec![Box::new(stderr_write), Box::new(vec_write)];
for mut w in writes {
w.write("hello".as_bytes());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment