Last active
March 11, 2018 17:26
-
-
Save joshlemer/87922f7bf8d0629f95bdb7c9dda71fe3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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