optional arguments

Matthew Fluet fluet@CS.Cornell.EDU
Wed, 6 Mar 2002 18:55:27 -0500 (EST)


Totally random thoughts.  I was cleaning directories and came across
Steve's Printf structure, based on Danvy's "Do we need dependent types?"
paper.  Somehow, this got me thinking whether or not the same sort of
trick could be used to give a reasonable interface to functions with
optional arguments.  By that, I mean having a function with some required
arguments and some optional arguments, which in their absense, default to
some default values.  For example, I have

    fun f {a, b, x} y = a * (real x) + b * (real y)
    val defs = {a = 0.0, b = 0.0, x = 0}

and somehow I derive a function f' such that I can do the following:

    val X = f' $ 1  ==>  0.0
    val Y = f' (` #b 1.0) $ 1  ==>  1.0
    val Z = f' (` #a 1.0) (` #x 2) (` #b 1.0) $ 1  ==>  3.0

Essentially, ` introduces an override for an optional argument while $
marks the end of optional arguments.  I came up with the code below, which
mostly works.  Since Standard ML doesn't have record polymorphism, it's a
little clumsy.  You'll notice that I don't use the Optional.make function
to lift f to f' in TestOptional.  I don't know how to get around the value
restriction; this, unfortunately, means that I need to expose the actual
type of Optional.t in OPTIONAL.

Anyways, nothing spectacular.  Probably not useful for everyday coding,
but I seem to remember that X Windows (and other toolkits) tend require
sets of attributes for which defaults are generally o.k. and it's a pain
to reconstruct the entire record to just override one or two fields.


signature OPTIONAL =
  sig
    type ('args, 'res, 'reps) t =
      ('args -> 'res) * 'args * 'reps
    type ('args, 'res, 'reps, 'k) u =
      (('args, 'res, 'reps) t -> 'k) -> 'k

    val make : ('args -> 'res) ->
               'args ->
               'reps -> 
               ('args, 'res, 'reps, 'k) u

    val ` : ('reps -> ('args -> 'a -> 'args)) -> 
            'a ->
            ('args, 'res, 'reps) t ->
            ('args, 'res, 'reps, 'k) u

    val $ : ('args, 'res, 'reps) t -> 'res
  end

functor TestOptional (S: OPTIONAL) =
  struct
    fun f {a, b, x} y = a * (real x) + b * (real y);
    val defs = {a = 0.0, b = 0.0, x = 0}
    local
      fun repa {a : 'a, b, x} (a' : 'a) = {a = a', b = b, x = x}
      fun repb {a, b : 'b, x} (b' : 'b) = {a = a, b = b', x = x}
      fun repx {a, b, x : 'x} (x' : 'x) = {a = a, b = b, x = x'}
    in
      val reps = {a = repa, b = repb, x = repx}
    end
   
    open S
    val f' = fn k => k (f, defs, reps)
    val X = f' $ 1
    val _ = print (concat ["f' $ 1 => ", 
                           Real.toString X, "\n"])
    val Y = f' (` #b 1.0) $ 1
    val _ = print (concat ["f' (` #b 1.0) $ 1 => ", 
                           Real.toString Y, "\n"])
    val Z = f' (` #a 1.0) (` #x 2) (` #b 1.0) $ 1
    val _ = print (concat ["f' (` #a 1.0) (` #x 2) (` #b 1.0) $ 1 => ",
                           Real.toString Z, "\n"])
  end

structure Optional : OPTIONAL =
  struct
    type ('args, 'res, 'reps) t =
      ('args -> 'res) * 'args * 'reps

    type ('args, 'res, 'reps, 'k) u = 
      (('args, 'res, 'reps) t -> 'k) -> 'k

    val make =
      fn f =>
      fn def =>
      fn reps =>
      fn k => k (f, def, reps)

    val using =
      fn s : 'reps -> ('args -> 'a -> 'args) =>
      fn v : 'a => 
      fn args : 'args => 
      fn reps : 'reps =>
      (s reps) args v
    fun ` s v = 
      fn (f, args, reps) =>
      fn k => 
      k (f, using s v args reps, reps)

    val $ = 
      fn (f, args, reps) =>
      f args
  end
			    
structure Z = TestOptional (Optional)