Re: How to use modules in place of Type Classes

Vesa Karvonen <vesa.karvonen-bbCR+/[email protected]>
Newsgroups gmane.comp.lang.sml.smlnj
Message-ID <[email protected]>
Quoting David Eger <eger-oTNwCEtKUwI/[email protected]>:
> I'm writing a library to do command line parsing in SML much like
> the C getopt() function.

Did you notice the GetOpt library that is part of the SML/NJ library (look
at the util directory)?

> I take a dictionary of expected arguments and their types and a command
> line string.  I return a dictionary of parsed arguments.
>
> I don't really care what sort of dictionary I'm passed.

Does it have to be a dictionary?

An alternative might be to return a list of the arguments and let the
caller create a dictionary from the list if desired.

Another alternative might be to treat argument parsing as a fold.  Your
getopt function would then have a type roughly of the form:

  ... -> ('argument * 'result -> 'result) -> 'result -> ...

If the user really wants to build a dictionary, then the result type would
be a dictionary type and the function would be the insert operation of the
dictionary type.

> In order for me to make a generalized GETOPT that accepts any sort of
> dictionary, it seems that I have to write my module as a functor, taking
> a structure argument and that each of my callers has to explicitly
> evaluate the functor.  This means that the implementation information
> about the underlying representation of a particular dictionary needs to
> be threaded through any code base that uses my library all the way down
> to the invocation of getopt().

If you decide to functorize your getopt implementation, then I would
assume that you would most likely instantiate that functor just once in a
given program.  This would look roughly like:

  structure Dict = ...
  structure GetOpt = MkGetOpt (Dict)

The rest of your program would directly refer to the Dict and GetOpt
bindings.  If you later want to change the dictionary implementation, you
would simply change the line that binds Dict.

> This seems to add a lot of programmer overhead.

That would be the case with library modules (modules that aren't specific
to any single application) that wish to access the options.  Modules that
are specific to a program can refer to the dictionary module directly.

> Is there a better way?

Depends.  I think that the getopt interface (or anything similar to it) is
rather low level.  Explicitly passing a dictionary of arguments around
also sounds like a chore and type-unsafe.

In many cases (not all) a process only gets/has a single global set of
command line arguments.  For such cases I would rather build a library
that allows one to concisely implement a "Control" module that specifies
the control flags (options) that the program uses.  For example, a program
implementing the (*nix) rm command would have a Control module with a
signature roughly of the following form:

  signature CONTROL = sig
     type 'a t
     (* Type of control flags, you would also provide at least a
        get -operation for reading control flags. *)

     val directory : bool t
     val force : bool t
     val interactive : bool t
     val preserveRoot : bool t
     val recursive : bool t
     val verbose : bool t
     val files : string list t

     (* ... *)
  end

The rest of the program would then refer to the bindings in the Control
module.  The benefit is that the options are now specified clearly in the
signature of the Control module and that your program code can not
accidentally refer to non-existent options or refer to an option by an
incorrect type.

BTW, this is roughly how the MLton compiler handles command line
arguments.  Also, try entering

  structure Foo = struct open Control end

in SML/NJ.

This approach can also be made to work in a multithreaded applications where
the control flags may be changed dynamically.  In such a case you would use
thread local variables for the control flags.

Library modules that wish to refer directly to the control flags could be
functorized and instantiated with the Control module.  Alternatively, such
library modules could implement their own control modules that could be
initialized by the main program (or they might just parse subsets of the
command line by themselves).  This way you could avoid functorizing the
library modules.

> Is there any equivalence to the type class sugar of Haskell that I could use?

No.  Well, not in the way that you probably think about.  Type classes can
be thought of as implicit "dictionary" (a record of values) passing.  In
SML you would pass the dictionary explicitly.

> In practice, do SML users just accept a very specific dictionary and have
> the caller do the manual conversions?  Do they functorize everything?

I think that SML users come in all shapes and sizes.  Some might just use
a specific dictionary implementation.  Some might functorize (almost)
everything.  Some wouldn't use a dictionary at all.

-Vesa Karvonen

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.