Reverse-engineering length-typed vectors
The last few posts posts followed a winding path toward a formulation of a type for length-typed vectors. In Fixing lists, I mused how something like lists could be a trie type. The Stream functor (necessarily infinite lists) is the natural trie for Peano numbers. The standard list functor [] (possibly finite lists) doesn’t seem to be a trie, since it’s built from sums. However, the functor Vec n of vectors ("fixed lists") of length n is built from (isomorphic to) products only (for any given n), and so might well be a trie.
Of what type is Vec n the corresponding trie? In other words, for what type q is Vec n a isomorphic to q → a (for all a).
Turning this question on its head, what simpler type gives rise to length-typed vectors in a standard fashion?
Edits:
- 2011-02-01: Define
Digits n basBNat n ↛ BNat b.
Deriving vectors
Recalling that Vec n a is an n-ary product of the type a, and forming the sort of derivation I used in Memoizing polymorphic functions via unmemoization,
Vec n a ≅ a × ⋯ × a
≅ (1 → a) × ⋯ × (1 → a)
≅ (1 + ⋯ + 1) → a
where the type 1 is what we call "unit" or "()" in Haskell, having exactly one (non-bottom) value. I used the isomorphism (a + b) → c ≅ (a → c) × (b → c), which corresponds to a familiar law of exponents: ca + b = ca × cb.
So the sought domain type q is any type isomorphic to 1 + ⋯ + 1, which is to say a type consisting of exactly n choices.
We have already seen a candidate for the index type q, of which Vec n is the natural trie functor. The post Type-bounded numbers defines a type BNat n corresponding to natural numbers less than n, where n is a type-encoded natural number.
data BNat ∷ * → * where
BZero ∷ BNat (S n)
BSucc ∷ BNat n → BNat (S n)
These two constructors correspond to two axioms about inequality: 0 < n + 1 and m < n ⇒ m + 1 < n + 1. The type BNat n then corresponds to canonoical proofs that m < n for various values of m, where the proofs are built out of the two axioms and the law of modus ponens (which corresponds to function application).
Assuming a type n is built up solely from Z and S, a simple inductive argument shows that the number of fully-defined elements (not containing ⊥) of BNat n is the natural number corresponding to n (i.e., nat ∷ Nat n, where nat is as in the post Doing more with length-typed vectors).
Vectors as numbers
We've seen that Vec is a trie for bounded unary numbers, i.e., BNat n ↛ a ≡ Vec n a, using the notation from Elegant memoization with higher-order types. (Note that I've changed my pretty-printing from "k :→: v" to "k ↛ v".)
It's also the case that a vector of digits can be used to represent numbers:
type Digits n b = Vec n (BNat b) -- n-digit number in base b
Or, more pleasing to my eye,
type Digits n b = BNat n ↛ BNat b
These representations can be given a little-endian or big-endian interpretation:
littleEndianToZ, bigEndianToZ ∷ ∀ n b. IsNat b ⇒ Digits n b → Integer
littleEndianToZ = foldr' (λ x s → fromBNat x + b * s) 0
where b = natToZ (nat ∷ Nat b)
bigEndianToZ = foldl' (λ s x → fromBNat x + b * s) 0
where b = natToZ (nat ∷ Nat b)
The foldl' and foldr' are from Data.Foldable.
Give it a try:
*Vec> let ds = map toBNat [3,5,7] ∷ [BNat TenT]
*Vec> let v3 = fromList ds ∷ Vec ThreeT (BNat TenT)
*Vec> v3
fromList [3,5,7]
*Vec> littleEndianToZ v3
753
*Vec> bigEndianToZ v3
357
It's a shame here to map to the unconstrained Integer type, since (a) the result must be a natural number, and (b) the result is statically bounded by bn.
Conal Elliott » Blog Archive » A trie for length-typed vectors:
[…] About « Reverse-engineering length-typed vectors […]
31 January 2011, 3:04 pm