Define a primitive data type: Difference between revisions

Haskell solution
(Haskell solution)
Line 90:
 
(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.
 
==[[Perl]]==
Anonymous user