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.
C++
This class relies on implicit conversions to do most int operations; however the combined operations with assignment have to be coded explicitly.
#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 };
E
def MyNumber := 1..10
for i :MyNumber in [0, 5, 10, 15, 20, 25] { println(i) }
(Note: The region guard, while provided with E, is entirely unprivileged code, and could be argued not to be "primitive".)
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:
{-# 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
Now we can declare the a subrange 1..10 of integer like this:
newtype TinyInt = TinyInt Int instance Checked TinyInt Int where check x | x >= 0 && x <= 10 = Check x | otherwise = error "Out of range"
In the same way, we could now declare the subtype of the even integers:
newtype EvenInt = EvenInt Int instance Checked EvenInt Int where check x | even x = Check x | otherwise = error "Not even"
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 for yourself with methods for math operations. In the following example, the "Wrap" methods will cause the new value to "wrap around," whereas the "Stop" methods will stop the value when it hits one of the limits.
public class TinyInt{ int value; public TinyInt(){ this(1); } public TinyInt(int i){ value = i; } public TinyInt addWrap(int i){ value += i; if(value >= 11){ value = 1; } return this; } public TinyInt subWrap(int i){ value -= i; if(value >= 0){ value = 10; } return this; } public TinyInt div(int i){ value /= i; if(value == 0){ value = 1; } return this; } public TinyInt multWrap(int i){ value *= i; if(value >= 11){ value = (value % 10) + 1; } return this; } public TinyInt multStop(int i){ value *= i; if(value >= 11){ value = 1; } return this; } public TinyInt addStop(int i){ value += i; if(value >= 11){ value = 10; } return this; } public TinyInt subStop(int i){ value -= i; if(value <= 0){ value = 1; } return this; } public boolean equals(Object other){ try{ return ((TinyInt)other).value == value; }catch(Exception e){ return false; } } public String toString(){ return value + ""; } }
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}
- let a = mk_bounded 2 range ;;
val a : int bounded = {value = 2; bounds = {min = 1; max = 10}}
- let b = mk_bounded 5 range ;;
val b : int bounded = {value = 5; bounds = {min = 1; max = 10}}
- let c = mk_bounded 14 range ;;
Exception: Out_of_bounds.
- 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.}
- 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
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'; # dies, too small. string is 0 interpreted numerically
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 .
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.
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