Define a primitive data type

From Rosetta Code
Revision as of 17:12, 21 September 2009 by rosettacode>Mwn3d (Whitespace, VB.NET highlighting might be broken? "End" isn't highlighted.)
Task
Define a primitive data type
You are encouraged to solve this task according to the task description, using any language you may know.

Demonstrate how to define a type that behaves like an integer but has a lowest valid value of 1 and a highest valid value of 10. Include all bounds checking you need to write, or explain how the compiler or interpreter creates those bounds checks for you.

Ada

type My_Type is range 1..10;

The compiler identifies the range of valid values from the range specification 1..10 and automatically builds in bounds checking where it is needed. The compiler is smart enough to omit bounds checking when it is not needed.

A : My_Type := 3;
B : My_Type := A;

The compiler will omit bounds checking for the assignment of A to B above because both values are of My_Type. A cannot hold a value outside the range of 1..10, therefore the assignment cannot produce an out of bounds result.

ALGOL 68

Built in or standard distribution routines

Bounded data types are not part of standard ALGOL 68, but can be implemented.

Implementation example

Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386
# assume max int <= ABS - max negative int #
INT max bounded = ( LENG max int * max int > long max int | ENTIER sqrt(max int) | max int );

MODE RANGE = STRUCT(INT lwb, upb);
MODE BOUNDED = STRUCT(INT value, RANGE range);
FORMAT bounded repr = $g"["g(-0)":"g(-0)"]"$;

# Define some useful operators for looping over ranges #
OP LWB = (RANGE range)INT: lwb OF range,
   UPB = (RANGE range)INT: upb OF range,
   LWB = (BOUNDED bounded)INT: lwb OF range OF bounded,
   UPB = (BOUNDED bounded)INT: upb OF range OF bounded;

PROC raise exception = ([]STRING args)VOID: (
  put(stand error, ("exception: ",args, newline));
  stop
);

PROC raise not implemented error := ([]STRING args)VOID: raise exception(args);
PROC raise bounds error := ([]STRING args)VOID: raise exception(args);

PRIO MIN=9, MAX=9;
OP MIN = ([]INT list)INT: ( 
  INT out:= list[LWB list];
  FOR index FROM LWB list+1 TO UPB list DO IF list[index]<out THEN out :=list[index] FI OD;
  out
);
OP MAX = ([]INT list)INT: ( 
  INT out:= list[LWB list];
  FOR index FROM LWB list+1 TO UPB list DO IF list[index]>out THEN out :=list[index] FI OD;
  out
);

PRIO ASSERTIN = 6;
OP ASSERTIN = (INT result, []RANGE range)BOUNDED: (
    BOUNDED out = (result, (MAX lwb OF range, MIN upb OF range));
    IF value OF out < lwb OF range OF out THEN
      raise bounds error(("out of bounds", whole(result, int width)," < [",whole(MAX lwb OF range, int width),":]"))
    ELIF value OF out > upb OF range OF out THEN
      raise bounds error(("out of bounds", whole(result, int width)," > [:",whole(MIN upb OF range, int width),"]"))
    FI;
    out
  ),
  ASSERTIN = (LONG INT result, []RANGE range)BOUNDED: (
    STRUCT (LONG INT value, RANGE range) out = (result, (MAX lwb OF range, MIN upb OF range));
    IF value OF out < lwb OF range OF out THEN
      raise bounds error(("out of bounds", whole(result, long int width)," < [",whole(MAX lwb OF range, int width),":]"))
    ELIF value OF out > upb OF range OF out THEN
      raise bounds error(("out of bounds", whole(result, long int width)," > [:",whole(MIN upb OF range, int width),"]"))
    FI;
    (SHORTEN value OF out, range OF out)
  ),
  ASSERTIN = (INT result, []BOUNDED bounds)BOUNDED: result ASSERTIN range OF bounds,
  ASSERTIN = (LONG INT result, []BOUNDED bounds)BOUNDED: result ASSERTIN range OF bounds;

INT half max int = max int OVER 2;
INT sqrt max int = ENTIER sqrt (max int);

OP + = (BOUNDED a, b)BOUNDED: 
         IF ABS value OF a < half max int AND ABS value OF b < half max int THEN
           value OF a + value OF b ASSERTIN []BOUNDED(a,b)
         ELSE
           LENG value OF a + value OF b ASSERTIN []BOUNDED(a,b)
         FI,
   - = (BOUNDED a, b)BOUNDED: value OF a + -value OF b ASSERTIN []BOUNDED(a,b),
   * = (BOUNDED a, b)BOUNDED: 
         IF ABS value OF a < sqrt max int AND ABS value OF b < sqrt max int THEN
           value OF a * value OF b ASSERTIN []BOUNDED(a,b)
         ELSE
           LENG value OF a * value OF b ASSERTIN []BOUNDED(a,b)
         FI,
   /  = (BOUNDED a, b)REAL: value OF a / value OF b,
   %  = (BOUNDED a, b)BOUNDED: value OF a % value OF b ASSERTIN []BOUNDED(a,b),
   %* = (BOUNDED a, b)BOUNDED: value OF a %* value OF b ASSERTIN []BOUNDED(a,b),
   ** = (BOUNDED a, INT exponent)BOUNDED: value OF a ** exponent ASSERTIN []BOUNDED(a);

OP OVER = (INT value, RANGE range)BOUNDED:
  IF ABS lwb OF range > max bounded THEN
    raise bounds error(("out of bounds, ABS", whole(lwb OF range, int width)," > [:",whole(max bounded, int width),"]"));
    SKIP
  ELIF ABS upb OF range > max bounded THEN
    raise bounds error(("out of bounds, ABS", whole(upb OF range, int width)," > [:",whole(max bounded, int width),"]"));
    SKIP
  ELSE
    value ASSERTIN []RANGE(range)
  FI;

OP INTINIT = (BOUNDED range)REAL: value OF range;

OP <  = (BOUNDED a, b)BOOL: value OF a < value OF b,
   >  = (BOUNDED a, b)BOOL: value OF a > value OF b,
   <= = (BOUNDED a, b)BOOL: NOT ( value OF a > value OF b ),
   >= = (BOUNDED a, b)BOOL: NOT ( value OF a < value OF b ),
   =  = (BOUNDED a, b)BOOL: value OF a = value OF b,
   /= = (BOUNDED a, b)BOOL: NOT (a = b);

# Monadic operators #
OP - = (BOUNDED range)BOUNDED: -value OF range ASSERTIN []BOUNDED(range),
   ABS = (BOUNDED range)BOUNDED: ABS value OF range ASSERTIN []BOUNDED(range);

COMMENT Operators for extended characters set, and increment/decrement "commented out" to save space. COMMENT

Test:

RANGE range = RANGE(0, 10000); 

# override the default exception #
raise bounds error := ([]STRING args)VOID: ( 
    putf(stand error, ($g$, args, $"- exiting to except bounds error"l$)); 
    except bounds error
  );

BOUNDED a, b := 0 OVER range;
FOR step FROM 4 BY 4 TO UPB range DO # something for pythagoras #
  b := b + step OVER range;
  a := ENTIER sqrt( 1.5 + 2 * value OF b ) OVER range OF b;
  printf(($"Sum of "$, bounded repr, a * a, b * b,
          $" is "$,    bounded repr, a * a + b * b, $l$))
OD;
except bounds error: 
  SKIP

Output:

Sum of          +9[0:10000]        +16[0:10000] is         +25[0:10000]
Sum of         +25[0:10000]       +144[0:10000] is        +169[0:10000]
Sum of         +49[0:10000]       +576[0:10000] is        +625[0:10000]
Sum of         +81[0:10000]      +1600[0:10000] is       +1681[0:10000]
Sum of        +121[0:10000]      +3600[0:10000] is       +3721[0:10000]
Sum of        +169[0:10000]      +7056[0:10000] is       +7225[0:10000]
out of bounds    +12544 > [:    +10000]- exiting to except bounds error

Other libraries or implementation specific extensions

As of February 2009 no open source libraries to do this task have been located.

C++

Works with: g++

This class relies on implicit conversions to do most int operations; however the combined operations with assignment have to be coded explicitly.

<lang cpp>#include <stdexcept>

class tiny_int { public:

 tiny_int(int i):
   value(i)
 {
   if (value < 1)
     throw std::out_of_range("tiny_int: value smaller than 1");
   if (value > 10)
     throw std::out_of_range("tiny_int: value larger than 10");
 }
 operator int() const
 {
   return value;
 }
 tiny_int& operator+=(int i)
 {
   // by assigning to *this instead of directly modifying value, the
   // constructor is called and thus the check is enforced
   *this = value + i;
   return *this;
 }
 tiny_int& operator-=(int i)
 {
   *this = value - i;
   return *this;
 }
 tiny_int& operator*=(int i)
 {
   *this = value * i;
   return *this;
 }
 tiny_int& operator/=(int i)
 {
   *this = value / i;
   return *this;
 }
 tiny_int& operator<<=(int i)
 {
   *this = value << i;
   return *this;
 }
 tiny_int& operator>>=(int i)
 {
   *this = value >> i;
   return *this;
 }
 tiny_int& operator&=(int i)
 {
   *this = value & i;
   return *this;
 }
 tiny_int& operator|=(int i)
 {
   *this = value | i;
   return *this;
 }

private:

 unsigned char value; // we don't need more space

};</lang>

Common Lisp

The built-in integer type specifier provides range parameters. deftype may be used to define an alias for it.

<lang lisp>(deftype one-to-ten ()

 '(integer 1 10))</lang>

For a bounds check, one may use typep (a predicate) or check-type (signals an error if not of the type).

<lang lisp>(defun word (i)

 (check-type i one-to-ten)
 (case i
   (1 "one")
   (2 "two")
   (3 "three")
   (4 "four")
   (5 "five")
   (6 "six")
   (7 "seven")
   (8 "eight")
   (9 "nine")
   (10 "ten")))</lang>

(Note that the above can be written without the separate check-type by using ecase instead of case, which signals an error when no case matches.)

To inform the compiler that a variable will be of a certain type, permitting optimizations, use a declaration:

<lang lisp>(dolist (i numbers)

 (declare (type one-to-ten i))
 ...)</lang>

Note, however, that the standard does not specify what happens in the event that a declaration is false (though SBCL, for example, does perform type checks on any declaration except when safety is 0); use check-type for portable bounds checks.

E

<lang e>def MyNumber := 1..10

for i :MyNumber in [0, 5, 10, 15, 20, 25] {

   println(i)

}</lang> (Note: The region guard, while provided with E, is entirely unprivileged code, and could be argued not to be "primitive".)

Fortran

Works with: Fortran version 90 and later

The module gives an example of how a bounded integer could be implemented in Fortran (not all the needed interfaces are implemented, and only the one for the + operator are shown). Bounds are checked at run-time.

<lang fortran>module Bounded

 implicit none
 type BoundedInteger
    integer, private :: v         ! we cannot allow direct access to this, or we
    integer, private :: from, to  !   can't check the bounds!
    logical, private :: critical
 end type BoundedInteger
 interface assignment(=)
    module procedure bounded_assign_bb, bounded_assign_bi !, &
                   ! bounded_assign_ib
 end interface
 interface operator(+)
    module procedure bounded_add_bbb !, bounded_add_bbi, &
                   ! bounded_add_bib, bounded_add_ibb,   &
                   ! bounded_add_iib, bounded_add_ibi,   &
                   ! bounded_add_bii
 end interface
 private :: bounded_assign_bb, bounded_assign_bi, &
            bounded_add_bbb

contains

 subroutine set_bound(bi, lower, upper, critical, value)
   type(BoundedInteger), intent(out) :: bi
   integer, intent(in) :: lower, upper
   integer, intent(in), optional :: value
   logical, intent(in), optional :: critical
   bi%from = min(lower, upper)
   bi%to = max(lower, upper)
   if ( present(critical) ) then
      bi%critical = critical
   else
      bi%critical = .false.
   end if
   if ( present(value) ) then
      bi = value
   end if
 end subroutine set_bound
 subroutine bounded_assign_bb(a, b)
   type(BoundedInteger), intent(out) :: a
   type(BoundedInteger), intent(in)  :: b
   call bounded_assign_bi(a, b%v)
 end subroutine bounded_assign_bb


 subroutine bounded_assign_bi(a, b)
   type(BoundedInteger), intent(out) :: a
   integer,              intent(in)  :: b
   if ( (a%from <= b) .and. (a%to >= b) ) then
      a%v = b
   else
      write(0,*) "BoundedInteger: out of bound assignment"
      if ( a%critical ) then
         stop 
      else
         if ( b < a%from ) then
            a%v = a%from
         else
            a%v = a%to
         end if
         write(0,"(A,' (',I0, ')')") "BoundedInteger: set to nearest bound", a%v
      end if
   end if
 end subroutine bounded_assign_bi


 function bounded_add_bbb(a, b) result(c)
   type(BoundedInteger) :: c
   type(BoundedInteger), intent(in) :: a, b
   integer :: t
   c%from = max(a%from, b%from)
   c%to   = min(a%to,   b%to)
   t = a%v + b%v
   if ( c%from <= t .and. c%to >= t ) then
      c%v = t
   else
      write(0,*) "BoundedInteger: out of bound sum"
      if ( a%critical .or. b%critical ) then
         stop
      else
         if ( t < c%from ) then
            c%v = c%from
         else
            c%v = c%to
         end if
         write(0,"(A,' (',I0,')')") "BoundedInteger: set to nearest bound", c%v
      end if
   end if
 end function bounded_add_bbb

end module Bounded</lang>

<lang fortran>program BoundedTest

 use Bounded
 implicit none
 type(BoundedInteger)     ::  a, b, c
 call set_bound(a, 1, 10)
 ! if we want to stop the program if a is out of bounds...
 ! call set_bound(a, 1, 10, critical=.true.)
 call set_bound(b, 1, 10)
 call set_bound(c, 1, 10)
 ! if we want to init c to a specific value...:
 ! call set_bound(c, 1, 10, value=6)
 a = 1         ! ok
 a = 4         ! ok
 a = -1        ! warning (a=1)
 a = 11        ! warning (a=10)
 a = 3         ! ok
 b = a         ! ok
 c = a + b     ! ok (3+3)
 c = c + a     ! ok (6+3=9)
 c = c + b     ! warning (c=10)

end program BoundedTest</lang>

Haskell

Haskell doesn't have any built-in subrange types. However, it is possible to declare arbitrary types that "behave like" any of the built-in types on the "usual" numeric etc. operations, because these operations are defined by type-classes. So we generalize the task a bit, and first declare a generic container type that supports an additional check operation. Then, we lift any operation in the base type to the container type, by executing the check after each operation:

<lang haskell>{-# OPTIONS -fglasgow-exts #-}

data Check a b = Check { unCheck :: b } deriving (Eq, Ord)

class Checked a b where

 check :: b -> Check a b

lift f x = f (unCheck x) liftc f x = check $ f (unCheck x)

lift2 f x y = f (unCheck x) (unCheck y) lift2c f x y = check $ f (unCheck x) (unCheck y) lift2p f x y = (check u, check v) where (u,v) = f (unCheck x) (unCheck y)

instance Show b => Show (Check a b) where

 show (Check x)        = show x
 showsPrec p (Check x) = showsPrec p x

instance (Enum b, Checked a b) => Enum (Check a b) where

 succ = liftc succ
 pred = liftc pred
 toEnum   = check . toEnum
 fromEnum = lift fromEnum

instance (Num b, Checked a b) => Num (Check a b) where

 (+) = lift2c (+)
 (-) = lift2c (-)
 (*) = lift2c (*)
negate = liftc negate
 abs    = liftc abs
  signum = liftc signum
 fromInteger = check . fromInteger

instance (Real b, Checked a b) => Real (Check a b) where

 toRational = lift toRational

instance (Integral b, Checked a b) => Integral (Check a b) where

 quot = lift2c quot
 rem  = lift2c rem
 div  = lift2c div
 mod  = lift2c mod       
 quotRem = lift2p quotRem
 divMod  = lift2p divMod
 toInteger = lift toInteger</lang>

Now we can declare the a subrange 1..10 of integer like this:

<lang haskell>newtype TinyInt = TinyInt Int

instance Checked TinyInt Int where

 check x | x >= 0 && x <= 10  =  Check x
         | otherwise          =  error "Out of range"</lang>

In the same way, we could now declare the subtype of the even integers: <lang haskell>newtype EvenInt = EvenInt Int

instance Checked EvenInt Int where
  check x | even x     =  Check x
          | otherwise  =  error "Not even"</lang>

Similarly, we could declare the subtype of floating point numbers with restricted exponent, and so on.

Java

The closest you can get to defining a primitive type in Java is making a new wrapper class with methods for math operations.

This example class throws an exception if the value is out of the bounds; it is implemented only in the assignment method "assign" and the addition method "add". The class can be easily extended.

<lang java>class BoundedIntOutOfBoundsException extends Exception {

 public BoundedIntOutOfBoundsException(int v, int l, int u) {
   super("value " + v + " is out of bounds [" + l + "," + u + "]");
 }

}

class BoundedInt {

 private int value;
 private int lower;
 private int upper;
 public BoundedInt(int l, int u) {
   lower = Math.min(l, u);
   upper = Math.max(l, u);
 }
 private boolean checkBounds(int v) {
   return ((v >= this.lower) && (v <= this.upper));
 }
 public void assign(BoundedInt i) throws BoundedIntOutOfBoundsException {{
   assign(i.value()); //could still throw Exception if the other BoundedInt has different bounds
 }
 public void assign(int v) throws BoundedIntOutOfBoundsException {
   if ( checkBounds(v) ) {
     this.value = v;
   } else {
     throw new BoundedIntOutOfBoundsException(v, this.lower, this.upper);
   }
 }
 public int add(BoundedInt i) throws BoundedIntOutOfBoundsException {
   return add(i.value());
 }
 public int add(int i) throws BoundedIntOutOfBoundsException {
   if ( checkBounds(this.value + i) ) {
     this.value += i;
   }  else {
     throw new BoundedIntOutOfBoundsException(this.value + i, this.lower, this.upper);
   }
   return this.value;
 }
 public int value() {
   return this.value;
 }

}


public class Bounded {

 public static void main(String[] args) throws BoundedIntOutOfBoundsException {
   BoundedInt a = new BoundedInt(1, 10);
   BoundedInt b = new BoundedInt(1, 10);
   a.assign(6);
   try {
     b.assign(12);
   } catch (Exception e) {
     System.out.print(e.getMessage() + "\n");
   }
   b.assign(9);
   try {
     a.add(b.value());
   } catch (Exception e) {
     System.out.print(e.getMessage() + "\n");
   }
 }

}</lang>

Modula-3

In Modula-3, subrange types are automatically subtypes of their base type, so if you define a type called MyInt to be the subrange [1..10] then MyInt is a subtype of INTEGER. If we defined MyChar as the subrange ['A'..'C'] then MyChar would be a subtype of CHAR. <lang modula3>TYPE MyInt = [1..10];</lang> MyInt can now be used anywhere an INTEGER can, such as the standard arithmetic functions. Trying to assign a value outside of the range of MyInt will result in a compile time warning, and/or a runtime error.

OCaml

<lang ocaml>exception Out_of_bounds

type 'a bounds = { min: 'a; max: 'a }

type 'a bounded = { value: 'a; bounds: 'a bounds }

let mk_bounds ~min ~max = { min=min; max=max } ;; (** val mk_bounds : min:'a -> max:'a -> 'a bounds *)

let check_bounds ~value ~bounds =

 if value < bounds.min || value > bounds.max then
   raise Out_of_bounds ;;

(** val check_bounds : value:'a -> bounds:'a bounds -> unit *)

let mk_bounded ~value ~bounds =

 check_bounds ~value ~bounds;
 { value=value; bounds=bounds } ;;

(** val mk_bounded : value:'a -> bounds:'a bounds -> 'a bounded *)

let op f a b =

 let res = f a.value b.value in
 if a.bounds <> b.bounds then
   invalid_arg "different bounds";      
 check_bounds res a.bounds;
 (mk_bounded res a.bounds)
 ;;            

(** val op : ('a -> 'a -> 'a) -> 'a bounded -> 'a bounded -> 'a bounded *)</lang>

Using in the interactive top level: <lang ocaml># let range = mk_bounds 1 10 ;; val range : int bounds = {min = 1; max = 10}

  1. let a = mk_bounded 2 range ;;

val a : int bounded = {value = 2; bounds = {min = 1; max = 10}}

  1. let b = mk_bounded 5 range ;;

val b : int bounded = {value = 5; bounds = {min = 1; max = 10}}

  1. let c = mk_bounded 14 range ;;

Exception: Out_of_bounds.

  1. op ( + ) a b ;;

- : int bounded = {value = 7; bounds = {min = 1; max = 10}}</lang>

which can be used with floats in the same way: <lang ocaml># let rf = mk_bounds 1.0 10.0 ;; val rf : float bounds = {min = 1.; max = 10.}

  1. let a = mk_bounded 2.2 rf
 and b = mk_bounded 5.6 rf in
 op ( +. ) a b ;;

- : float bounded = {value = 7.8; bounds = {min = 1.; max = 10.}}</lang>

Perl

Works with: Perl version 5

<lang perl>package One_To_Ten; use Carp qw(croak); use Tie::Scalar qw(); use base qw(Tie::StdScalar);

sub STORE {

   my $self = shift;
   my $val = int shift;
   croak 'out of bounds' if $val < 1 or $val > 10;
   $$self = $val;

};

package main; tie my $t, 'One_To_Ten'; $t = 3; # ok $t = 5.2; # ok, silently coerced to int $t = -2; # dies, too small $t = 11; # dies, too big $t = 'xyzzy';

  1. dies, too small. string is 0 interpreted numerically</lang>

Python

This doesn't really apply as Python names don't have a type, but something can be done: <lang python>>>> class num(int):

   def __init__(self, b):
       if 1 <= b <= 10:
           return int.__init__(self+0)
       else:
           raise ValueError,"Value %s should be >=0 and <= 10" % b


>>> x = num(3) >>> x = num(11)

Traceback (most recent call last):

 File "<pyshell#394>", line 1, in <module>
   x = num(11)
 File "<pyshell#392>", line 6, in __init__
   raise ValueError,"Value %s should be >=0 and <= 10" % b

ValueError: Value 11 should be >=0 and <= 10 >>> x 3 >>> type(x) <class '__main__.num'> >>> </lang>

Ruby

ref http://codeidol.com/other/rubyckbk/Numbers/Simulating-a-Subclass-of-Fixnum/

Some object-oriented languages won't let you subclass the "basic" data types
like integers. Other languages implement those data types as classes, so you
can subclass them, no questions asked. Ruby implements numbers as classes
(Integer, with its concrete subclasses Fixnum and Bignum), and you can subclass
those classes. If you try, though, you'll quickly discover that your subclasses
are useless: they don't have constructors.  

Ruby jealously guards the creation of new Integer objects. This way it ensures
that, for instance, there can be only one Fixnum instance for a given number

The easiest way to delegate all methods is to create a class that's nearly empty
and define a method_missing method.

<lang ruby>require 'test/unit' include Test::Unit::Assertions

class MyInt

 @@min = 1
 @@max = 10
 
 attr_reader :value
 private :value
 
 def initialize(val)
   begin
     v = Integer(val)
   rescue ArgumentError
     raise ArgumentError, "invalid value '#{val}', must be an integer"
   end
   
   unless v.between?(@@min, @@max)
     raise ArgumentError, "invalid value '#{v}', must be between #{@@min} and #{@@max}"
   end
   
   @value = v
 end
 
 def method_missing(m, *args)
   super unless @value.respond_to?(m)
   myint_args = args.collect do |arg|
     arg.kind_of?(self.class) ? arg.to_int : arg
   end
   result = @value.send(m, *myint_args)
   return result if m == :coerce
   case result
   when Integer
     MyInt.new(result)
   when Array
     result.collect do |element|
       element.kind_of?(Integer) ? MyInt.new(element) : element
     end
   else
     result
   end
 end
 
 def respond_to?(method)
   super or @value.respond_to? method
 end
 
 def to_int
   @value
 end
 def to_f
   Float(@value)
 end
 def to_s
   @value.to_s
 end
 def inspect
   to_s
 end

end


assert_raise(ArgumentError) { MyInt.new("foo") } # => invalid value 'foo', must be an integer assert_raise(ArgumentError) { MyInt.new(11) } # => invalid value '11', must be an integer

a = MyInt.new(7) b = MyInt.new(5)

c = 5 + a assert_kind_of(Fixnum, c) assert_equal(12, c)

c = a + 2 assert_kind_of(MyInt, c) assert_equal(9, c.to_int)

c = a + 2.8 assert_kind_of(Float, c) assert_equal(9.8, c)

c = a - b assert_kind_of(MyInt, c) assert_equal(2, c.to_int)

assert_raise(ArgumentError) { c = a + b } # => invalid value '12', must be an integer assert_raise(ArgumentError) { c = b - a } # => invalid value '-2', must be an integer</lang>

Tcl

Tcl does not attach types to values or variables, but it does allow the programmer to create traces on variables that can be used to enforce type-like constraints among other things. Traces are procedures that execute when variables are read, written and/or unset. (Traces are also available for commands and for the execution of a script.) Tcl's compiler does not enforce these constraints; they're strictly runtime entities. <lang tcl>namespace eval ::myIntType {

   variable value_cache
   array set value_cache {}
   variable type integer
   variable min 1
   variable max 10
   variable errMsg "cannot set %s to %s: must be a $type between $min and $max"

} proc ::myIntType::declare varname {

   set ns [namespace current]
   uplevel [list trace add variable $varname write ${ns}::write]
   uplevel [list trace add variable $varname unset ${ns}::unset_var]

} proc ::myIntType::unset_var {varname args} {

   variable value_cache
   unset value_cache($varname)

} proc ::myIntType::validate {value} {

   variable type
   variable min
   variable max
   expr {[string is $type -strict $value] && $min <= $value && $value <= $max}

} proc ::myIntType::write {varname args} {

   variable value_cache
   upvar $varname var
   set value $var
   if {[validate $value]} {
       set value_cache($varname) $value
   } else {
       if {[info exists value_cache($varname)]} {
           set var $value_cache($varname)
       }
       variable errMsg
       error [format $errMsg $varname $value]
   }

}</lang>

So, in an interactive tclsh we can see:

% myIntType::declare foo
% set foo ;# regular Tcl error:  foo is declared but still unset
can't read "foo": no such variable
% set foo bar
can't set "foo": cannot set foo to bar: must be a integer between 1 and 10
% set foo 3
3
% incr foo 10
can't set "foo": cannot set foo to 13: must be a integer between 1 and 10
% incr foo -10
can't set "foo": cannot set foo to -7: must be a integer between 1 and 10
% set foo 0
can't set "foo": cannot set foo to 0: must be a integer between 1 and 10
% set foo
3
% set foo [expr {$foo * 1.5}]
can't set "foo": cannot set foo to 4.5: must be a integer between 1 and 10
% set foo
3
% unset foo
% 

Toka

<lang toka>needs quotes {

 variable update
 [ update @ [ ! ] [ @ ] ifTrueFalse update off ] is action
 [ dup >r 0 11 r> within [ update on ] [ drop ." Out of bounds\n " ] ifTrueFalse ]
 [ ` [ invoke cell-size malloc # ` action compile ` ] invoke is ]

} is value:1-10:

 is to

value:1-10: foo 1 to foo foo .</lang>

Visual Basic .NET

Visual Basic has full support for creating your own primitives, but every operator has to be implemented explicitly. Often developers will only implement the parts they are using and skip the rest.

Also note that some operators return a Double instead of a new LimitedInt. This was by choice in order to match the behavior of Integers in VB.

<lang vbnet>Structure LimitedInt

 Implements IComparable(Of LimitedInt)
 Implements IEquatable(Of LimitedInt)
 Private m_Value As Integer 'treat the default, 0 as being really 1
 Public ReadOnly Property Value() As Integer
   Get
     Return If(m_Value = 0, 1, m_Value)
   End Get
 End Property
 Public Sub New(ByVal value As Integer)
   If value < 1 Or value > 10 Then Throw New ArgumentOutOfRangeException("value")
   m_Value = value
 End Sub
 Public Function CompareTo(ByVal other As LimitedInt) As Integer Implements System.IComparable(Of LimitedInt).CompareTo
   Return Me.Value - other.Value
 End Function

 Public Overloads Function Equals(ByVal other As LimitedInt) As Boolean Implements System.IEquatable(Of LimitedInt).Equals
   Return Me.Value = other.Value
 End Function
 Public Overrides Function GetHashCode() As Integer
   Return Value.GetHashCode
 End Function
 Public Overrides Function Equals(ByVal obj As Object) As Boolean
   If TypeOf obj Is LimitedInt Then Return CType(obj, LimitedInt) = Me
 End Function
 Public Shared Operator =(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean
   Return left.Equals(right)
 End Operator
 Public Shared Operator <>(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean
   Return Not (left = right)
 End Operator
 Public Shared Operator +(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value + right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator -(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value - right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator *(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value * right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator /(ByVal left As LimitedInt, ByVal right As LimitedInt) As Double
   Return left.Value / right.Value
 End Operator
 Public Shared Operator \(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value \ right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator Mod(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value Mod right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator And(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value And right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator Or(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value Or right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator Xor(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt
   Dim temp As Integer = left.Value Xor right.Value
   Select Case temp
     Case 1 To 10 : Return New LimitedInt(temp)
     Case Else : Throw New OverflowException
   End Select
 End Operator
 Public Shared Operator ^(ByVal left As LimitedInt, ByVal right As LimitedInt) As Double
   Return left.Value ^ right.Value
 End Operator
 Public Shared Operator <(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean
   Return left.Value < right.Value
 End Operator
 Public Shared Operator >(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean
   Return left.Value > right.Value
 End Operator
 Public Shared Operator <=(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean
   Return left.Value <= right.Value
 End Operator
 Public Shared Operator >=(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean
   Return left.Value >= right.Value
 End Operator
 Public Shared Widening Operator CType(ByVal left As LimitedInt) As Integer
   Return left.Value
 End Operator
 Public Shared Narrowing Operator CType(ByVal left As Integer) As LimitedInt
   Return New LimitedInt(left)
 End Operator

End Structure</lang>