-
Notifications
You must be signed in to change notification settings - Fork 571
Closed
Milestone
Description
Currently various encodings of type-level natural numbers are in use, which both have downsides in terms of performance:
- Peano encoding quickly results in huge types, and reflection requires O(n) time.
- Base-10 encoding results in smaller types, but still requires O(log_10 n) time.
They also don't result in the prettiest types, such as S (S (S (S Z))) for 4 or D4 :* D2 for 42. It would be nice to have type-level natural numbers with nice syntax and better performance, both at compile-time and at runtime. Similar to GHC.TypeLits.
Natural number types
- Add a new kind to
PrimcalledNat. - Allow nonnegative integer literals in types. Each distinct such literal is a distinct type. The kind is
Prim.Nat.
Reflection and arithmetic library
Add a new PureScript library to core with the following module:
module Data.Nat where
import Data.Int as Int
import Prelude
class IsNat (n :: Nat) where
reflectNat :: proxy n -> Int
instance isNatMagic :: IsNat <magic> where
reflectNat = <magic>
foreign import data Add :: Nat -> Nat -> Nat
foreign import data Sub :: Nat -> Nat -> Nat
foreign import data Mul :: Nat -> Nat -> Nat
foreign import data Div :: Nat -> Nat -> Nat
foreign import data Pow :: Nat -> Nat -> Nat
instance isNatAdd :: (IsNat a, IsNat b) => IsNat (Add a b) where
reflectNat _ = reflectNat @a + reflectNat @b
instance isNatSub :: (IsNat a, IsNat b) => IsNat (Sub a b) where
reflectNat _ = clamp 0 top $ reflectNat @a - reflectNat @b
instance isNatMul :: (IsNat a, IsNat b) => IsNat (Mul a b) where
reflectNat _ = reflectNat @a * reflectNat @b
instance isNatDiv :: (IsNat a, IsNat b) => IsNat (Div a b) where
reflectNat _ = reflectNat @a / reflectNat @b
instance isNatPow :: (IsNat a, IsNat b) => IsNat (Pow a b) where
reflectNat _ = Int.pow (reflectNat @a) (reflectNat @b)
infixl 6 type Add as +
infixl 6 type Sub as -
infixl 7 type Mul as *
infixl 7 type Div as /Where <magic> means "any non-negative type-level integer literal".
Example
module Data.Vect
( Vect
, empty
, cons
, uncons
, append
) where
newtype Vect (n :: Nat) a = Vect (List a)
empty :: forall a. Vect 0 a
empty = Vect Nil
cons :: forall a n. a -> Vect n a -> Vect (n + 1) a
cons a (Vect b) = Vect (a : b)
uncons :: forall a n. Vect (n + 1) a -> {head :: a, tail :: Vect n a}
uncons (Vect a) =
let {head, tail} = unsafePartial fromJust $ List.uncons a
in {head, tail: Vect tail}
append :: forall a n m. Vect n a -> Vect m a -> Vect (n + m) a
append (Vect a) (Vect b) = Vect (a <> b)Open issues
- How does
reflectNatdeal with integer overflow? - How does
isNatSubdeal withb > a? - How does
isNatDivdeal with division by zero? - Logic languages such as Mercury are aware of arithmetic and unify accordingly.
- Should unification be aware of the associativity and commutativity of addition and multiplication?
- Should unification perform constant folding?
- We can bikeshed elsewhere about the exact names of modules, types, and kinds.
Pauan, paf31, hdgarrood, maxfii, rvion and 9 more