[MLton] using fold to write numeric constants

Stephen Weeks MLton@mlton.org
Fri, 10 Feb 2006 07:34:58 -0800


The following code shows how to use fold to conveniently write numeric
constants in any base of various types.  For example,

  N i 10 `1`2`3 $

denotes 123:int in base 10, while

  N ii 8 `2`3 $

denotes 19:IntInf.int in base 8.

Digits are checked dynamically to ensure they are valid for the base,
but of course MLton will simplify everything away at compile time.  I
can imagine using phantom types to statically ensure validity, at the
expense of (perhaps too much) extra verbiage.

--------------------------------------------------------------------------------

fun $ (a, f) = f a
fun curry f x y = f (x, y)
fun id x = x
fun pass x f = f x

structure Fold =
   struct
      val fold = pass
      fun step0 h (a1, f) = fold (h a1, f)
      fun step1 h $ x = step0 (curry h x) $
   end

structure Num =
   struct
      fun N ty base = Fold.fold (ty base, fn (n, _) => n)

      local
         fun make (zero, op *, op +, op <, op <=, i2x, x2s) base =
            let
               val base = i2x base
            in
               (zero, fn (i, n) =>
                let
                   val i = i2x i
                in
                   if zero <= i andalso i < base then
                      n * base + i
                   else
                      raise Fail (concat
                                  ["Num: ", x2s i,
                                   " is not a valid digit in base ", x2s base])
                end)
            end
      in
         val i = make (0, op *, op +, op <, op <=, id, Int.toString)
         val ii = make (0, op *, op +, op <, op <=,
                        IntInf.fromInt, IntInf.toString)
         val w = make (0w0, op *, op +, op <, op <=,
                       Word.fromInt, Word.toString)
      end

      fun ` z = Fold.step1 (fn (i, (ac, step)) => (step (i, ac), step)) z

      val a = 10
      val b = 11
      val c = 12
      val d = 13
      val e = 14
      val f = 15
   end

local
   fun make x2s x = print (concat [x2s x, "\n"])
in
   val pi = make Int.toString
   val pii = make IntInf.toString
   val pw = make Word.toString
end

local
   open Num
in
   val () = pi (N i 10 `1`2`3 $)
   val () = pii (N ii 10 `1`2`3`4`5`6`7`8`9`0`1`2 $)
   val () = pw (N w 16 `1`b $)
   val () = pi (N i 12 `1`a $)
   val () = pii (N ii 8 `2`3 $)
   val () = pw (N w 2 `1`1`0 $)
end