scala - How do I enable my desired semantics on a natural numbers value class? -


for project working on want create class representing count of resources. in future might become collection of counts of different resource types (hence not coding resources class value class), single (anonymous) resource type sufficient.

this resources count should never negative, though, want restrict values can have set of natural numbers (ie. non-negative integers). looking @ creating new value class called nat.

a couple of semantic points want achieve:

  • if try create nat negative value, should exception thrown.
  • if try add int (or nat) existing nat, should work, truncating value 0 if int passed in large enough negative number - no exception thrown!

this means def +(nat: nat), want form of def +(int: int), otherwise int passed + converted nat first, cause exception. because nat value class, though, these 2 methods have same signature after erasure, won't work.

i tried def +(int: richint), hoping implicit conversion take precedence, richint value class same problem ensues.

one work around did discover use 1 of traits mixed in richint, specifically, orderedproxy. int implicitly converted richint , passed method orderedproxy (in form not recognised value class) in preference being converted nat, , semantics want.

thus, code far looks following:

import runtime.{integralproxy, orderedproxy}  class nat private(val self: int) extends anyval integralproxy[int] {   protected def num = scala.math.numeric.intisintegral   protected def ord = scala.math.ordering.int    import nat._   def iszero = (this == zero)   def +(nat: nat): nat = nat(self + nat.self)   def +(int: orderedproxy[int]): nat = trunc(self + int.self)   def -(nat: nat): nat = trunc(self - nat.self)   def -(int: orderedproxy[int]): nat = trunc(self - int.self)   def -%(nat: nat) = (this - nat).self match { // create tuple reduced count of minuend, plus remainder subtrahend if minuend zero.     case 0 => (zero, (nat - this))     case nonzero => (nat(nonzero), zero)   } }  object nat {   val neg_param_msg = "cannot assign negative value"    val zero: nat = nat(0)    def apply(value: int): nat = value match {       case cnt if (cnt < 0) => throw new runtimeexception(neg_param_msg)       case 0 => 0       case cnt => new nat(cnt)     }    def apply(value: long): nat = apply(value.toint)    def trunc(value: int): nat = value match {       case cnt if (cnt <= 0) => 0       case cnt => new nat(cnt)     }    def trunc(value: long): nat = trunc(value.toint) }  trait resourcescomponent {   import nat._    sealed case class resources(count: nat)   {     import resources._      require(count != 0 || hasnone)      def hasnone = (this == none)     def +(res: resources) = resources(count + res.count)     def -(res: resources) = resources(count - res.count)     def -%(res: resources) = (count - res.count).self match { // similar -% nat, convert tuple of resources - there better (eg. '.map'-like) way this?       case 0 => (none, resources(res.count - count))       case leftover => (resources(leftover), none)     }   }    object resources   {     val neg_res_msg = "cannot assign negative resources"      def apply(value: orderedproxy[int]) = value.self match {         case cnt if (cnt < 0) => throw new runtimeexception(neg_res_msg)         case 0 => none         case cnt => new resources(nat(cnt))       }      object none extends resources(zero)     {       override def hasnone = true       override def +(res: resources) = res       override def -(res: resources) = none       override def -%(res: resources) = (none, res)     }   } } 

as say, seems work, work around feels bit kludgy. suggestions on improving it?

the solution simple: truncate 0 when constructing nats negative integers. apart being simpler, solution more consistent. don't see why anat + -1 work differently anat + nat(-1) (including case of both throwing same exception). in fact, programming language telling consistency problem exists, forcing complex, unnatural construct.

if want make difference between ints , nats, don't try trick language (and other developers!). honest , define different operator ints. not overload. suggested name: safeadd, intadd, or similar.


Comments

Popular posts from this blog

c# - Send Image in Json : 400 Bad request -

jquery - Fancybox - apply a function to several elements -

An easy way to program an Android keyboard layout app -