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)