Define a primitive data type: Difference between revisions
Content added Content deleted
m (→{{header|C++}}: Technically, they would have used g++, not gcc. Both are part of GCC, though...) |
m (Alphabetized) |
||
Line 85: | Line 85: | ||
(Note: The region guard, while provided with E, is entirely unprivileged code, and could be argued not to be "primitive".) |
(Note: The region guard, while provided with E, is entirely unprivileged code, and could be argued not to be "primitive".) |
||
=={{header|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. |
|||
=={{header|Java}}== |
=={{header|Java}}== |
||
Line 167: | Line 234: | ||
} |
} |
||
} |
} |
||
=={{header|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. |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |