Last active
December 26, 2015 17:59
-
-
Save mjhopkins/7191232 to your computer and use it in GitHub Desktop.
Typeclass demo (with inheritance)
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
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