Skip to content

Instantly share code, notes, and snippets.

@mjhopkins
Last active December 26, 2015 17:59
Show Gist options
  • Save mjhopkins/7191232 to your computer and use it in GitHub Desktop.
Save mjhopkins/7191232 to your computer and use it in GitHub Desktop.
Typeclass demo (with inheritance)
import com.google.protobuf.GeneratedMessage
import org.apache.hadoop.hdfs.protocol.proto.ClientNamenodeProtocolProtos.{RenameRequestProto, AppendResponseProto}
import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.ClientReadStatusProto
import scalaz.{Tag, @@}
/*
Type class usage demo
*/
object byteArrayable {
// syntax to make usage a little more natural
// we're effectively monkey patching the method onto a, with behaviour defined per-type in our type class instances
// but in a way that the compiler will stop us if we're doing something wrong
implicit def ByteArrayableSyntax[A](a: A)(implicit ba: ByteArrayable[A]) = new {
def toByteArray = ba.toByteArray(a)
}
}
// we'll provide a different implementation for BigInt instances tagged as LittleEndian
trait LittleEndian
def LittleEndian(i: BigInt): BigInt @@ LittleEndian = Tag[BigInt, LittleEndian](i)
// our type class
trait ByteArrayable[-A] {
def toByteArray(a: A): Array[Byte]
}
// usage
object ByteArrayableTest {
import byteArrayable.ByteArrayableSyntax
def accept[A: ByteArrayable](a: A) = {
println(a.toByteArray)
}
val c: ClientReadStatusProto = null
val a: AppendResponseProto = null
val r: RenameRequestProto = null
accept(c)
accept(a)
accept(r)
accept(123)
accept(BigInt(4098)) // prints Array(16, 2)
accept(LittleEndian(BigInt(4098))) // prints Array(2, 16)
}
object ByteArrayable {
// define some instances
implicit val generatedMessageByteArrayable = new ByteArrayable[GeneratedMessage] {
def toByteArray(a: GeneratedMessage) = a.toByteArray
}
implicit val intByteArrayable = new ByteArrayable[Int] {
def toByteArray(a: Int) = Array(a.toByte)
}
implicit val bigDecimalByteArrayable = new ByteArrayable[BigInt] {
def toByteArray(a: BigInt) = a.toByteArray
}
implicit val bigDecimalLittleEndianByteArrayable = new ByteArrayable[BigInt @@ LittleEndian] {
def toByteArray(a: @@[BigInt, LittleEndian]) = a.toByteArray.reverse
}
}
/*
NB the "-" variance annotation on ByteArrayable is there so that we can provide an implementation for a type X,
and it'll work for subtypes of X
It's "-" because the parameter of type A is an input to "toByteArray" (it occurs in "contravariant position")
If our trait instead had methods that produced an A as output, we'd need a "+" (covariant)
and if it both consumed and produced A's, we'd need to leave off the annotation (invariant)
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment