This is not impossible - just very ugly. We need natural numbers
data Nat = Z | S Nat
and limited natural numbers
data Bounded (n :: Nat) where BZ :: Bounded n BS :: Bounded n -> Bounded (S n)
Then your function should be something like
succ :: Bounded n -> Bounded n succ bn = fromMaybe BZ $ go bn where go :: Bounded n -> Maybe (Bounded n) go = ...
In go we need
- map
BZ to Nothing if n is Z (i.e. if a Bounded reaches its maximum and overflows) - map
BZ to Just (BS BZ) if n not Z (i.e. if a Bounded not reached its maximum). - calling
go recursively for the BS case.
However, the problem is that at the level of values ββit is impossible to get n . Haskell is not dependent on this. A common hack is using singletons . Manual Recording
data Natty (n :: Nat) where Zy :: Natty Z Sy :: Natty n -> Natty (S n) class NATTY (n :: Nat) where natty :: Natty n instance NATTY Z where natty = Zy instance NATTY n => NATTY (S n) where natty = Sy natty
Now we can get a representation of level n at the level of the value in Bounded n in go :
succ :: NATTY n => Bounded n -> Bounded n succ bn = fromMaybe BZ $ go natty bn where go :: Natty n -> Bounded n -> Maybe (Bounded n) go Zy BZ = Nothing go (Sy ny) BZ = Just (BS BZ) go (Sy ny) (BS bn) = BS <$> go ny bn
And a class like NATTY used to output this value automatically.
Some tests:
instance Eq (Bounded n) where BZ == BZ = True BS bn == BS bm = bn == bm _ == _ = False zero :: Bounded (S (SZ)) zero = BZ one :: Bounded (S (SZ)) one = BS BZ two :: Bounded (S (SZ)) two = BS (BS BZ) main = do print $ succ zero == zero -- False print $ succ zero == one -- True print $ succ one == two -- True print $ succ two == zero -- True
code .
Using the singletons library, we can define succ as
$(singletons [d| data Nat = Z | S Nat deriving (Eq, Show) |]) data Bounded n where BZ :: Bounded n BS :: Bounded n -> Bounded (S n) succ :: SingI n => Bounded n -> Bounded n succ bn = fromMaybe BZ $ go sing bn where go :: Sing n -> Bounded n -> Maybe (Bounded n) go SZ BZ = Nothing go (SS ny) BZ = Just (BS BZ) go (SS ny) (BS bn) = BS <$> go ny bn
As for raising data at the level level, there are two approaches: CPS and existential types .