I am looking to create a type family that would represent data sizes (Byte, KB...). for that, the idea is to build a base type to have the real sizes based on:
type SizeUnit = Int
type B = SizeUnit
type KB = SizeUnit
type MB = SizeUnit
type GB = SizeUnit
type TB = SizeUnit
type PB = SizeUnit
type EB = SizeUnit
type ZB = SizeUnit
type YB = SizeUnit
have an ordered list of them:
val sizes = List(B, KB, MB, GB, TB, PB, EX, ZB, TB)
and have a convertion method that takes a target type, finds the index difference between them and multiplies by 1024 in the power of difference. so:
def convertTo(targetType: SizeUnit): SizeUnit ={
def power(itr: Int): Int = {
if (itr == 0) 1
else 1024*power(itr-1)
}
val distance = sizes.indexOf(targetType) - sizes.indexOf(this)
distance match {
//same type - same value
case 0 => targetType
//positive distance means larget unit - smaller number
case x>0 => targetType / power(distance)
//negative distance means smaller unit - larger number and take care of negitivity
case x<0 => targetType * power(distance) * (-1)
}
}
i have a few problems before i even check the validity of the method (as i am new to Scala):
- is there a way to create a List (or any other Seq) that holds types rather than values? or rather - types as values?
- if i understand correctly, types are not kept beyond compilation. does that mean that in runtime, if i pass a GB value to an existing KB, it cannot decipher the types?
thank you, Ehud
All those types are simply type aliases, not independent types.
Type aliases are simply different names for the same type. So even if you could write it, your list would be equivalent to having written:
And similarly, you could never use these types to write a function that is required to accept a quantity in MB, since all of these types are the same thing.
To separate out B, KB, MB, etc as different "kinds" of integer, you would need them to be subtypes of
Int
, not type aliases forInt
. ButInt
is a final type, so you can't subtype it anyway.A much better approach is to just let
Int
represent a raw number, and instead implement a type that represents anInt
together with a unit. There are several approaches you can take for this, but I'd do it something like this:Now 3 megabytes is
Storage(3, MB)
and 17 bytes isStorage(17, B)
. You have nice statically enforced separation between arbitrary integers andStorage
quantities, and you always have the unit as a data object (no need to be able to statically infer it) whenever you have aStorage
quantity. You can put the objectsB
,KB
,MB
, etc in a list, and do whatever manipulation with them you want.Alternatively you could make the unit objects themselves contain some information about their order or ratios between them, rather than storing that information in an external list.
You can even do wacky things with implicit conversions using this scheme. Something like this springs to mind:
With that, once you've imported the implicit, you can simply write things like
4 MB
or123456789 B
instead ofStorage(4, MB)
orStorage(123456789, B)
.