michele.simionato@[EMAIL PROTECTED]
<michele.simionato@[EMAIL PROTECTED]
> wrote:
> On Dec 2, 1:29 pm, Vesa Karvonen <vesa.karvo...@[EMAIL PROTECTED]
> wrote:
[...]
> > [...] So, introspection isn't just a blessing --- it is also a
> > limitation.
> I don't buy your argument. A few bytes spent in docstrings are a
> non-issue when we all have hundreds of gigabytes of unused disk space.
I just noted that introspection *alone* is not sufficient. Most of my
comment is concerned with the disadvantages of run-time introspection, not
docstrings. I'm certainly not suggesting that introspection or docstrings
wouldn't have advantages. I'm just noting that introspection also has
costs and that many uses of introspection really aren't that difficult to
encode or are even counter productive. In particular, I think that the
approach I'm using for test specification is more expressive (users can
write their own shorthands) and less fragile and ad hoc (not dependent on
an ad hoc naming convention) than the use of introspection to grep for
functions matching a particular kind of name --- while being roughly
equally syntactically lightweight for simple uses and more concise with
user defined abbreviations.
I've never really felt the need for run-time introspection in SML. In my
experience, introspection (and stuff like aspect oriented programming)
tends to lead to fragile, non composable, hard to understand and debug, ad
hoc hacks. Introspection also conflicts (well, requires careful
consideration) with type checking, security, and abstraction, but I'm not
going to go into that (I know that there are a couple of guys here who
have written theses on related subjects).
> And even if some dead code was not removed by the compiler, would it
> matter much, in practice?
It is not just about some dead code. As I hinted earlier, run-time
introspection makes it much harder to perform effective data
representation optimizations. Introspection also interferes with
control flow optimizations. Such optimizations are very im****tant for
making SML programs run fast.
> I am of the opinion that if an user wants an optimization, it should say
> so explicitely with a compiler flag: by default he should not pay the
> price of the optimization.
That is an interesting opinion. If you want to use a slow implementation,
you can choose from multiple SML implementations that do not optimize
aggressively. Some of those implementations do offer some limited forms
of introspection. Also, I think that Common Lisp would probably be a good
fit to your ideology --- you might want to take a look at it.
Really, you can't have everything. I can easily imagine, and have seen
several times in other contexts, the opposite of your argument. I've seen
people wondering why it takes forever to start an application or why
binaries are so large. Heck, I've personally had the "pleasure" of using
a few (Java) applications that were simply ridiculously slow, both at
starting up and during normal operation, even with a rather powerful
machine (this was actually quite recently).
Let me elaborate on why my preference is the opposite of yours. I love
abstraction. I do not hesitate to create even small utility functions to
eliminate repetition from my code. Often these functions are higher-order
functions. When I write applications, I continuously look for stuff that
could be moved into libraries. When designing a library, I try to make
the interface of the library as convenient to the user as possible. Often
this means that I look for ways to make the library into essentially a
domain specific language for expressing specifications for program
components. SML is quite good at this kind of programming (but I'll omit
the arguments).
The downside of programming abstractly is efficiency. Layers of
abstraction tend to have costs that ac***ulate (each layer adds to the
costs). This is called abstraction penalty and is a very real impediment
in some application domains and languages (or their implementations). I
used to program games (professionally for about 6 years), and, in
particular, libraries for writing games, in C++ (and even assembly
language). (I've written hundreds of thousands of lines of C++.) For
example, I've written libraries for rendering real-time graphics. One of
the main reasons for using a language like C++ for such work is
performance (other main reasons are availability of compilers on target
platforms and social factors (everyone on the team knows the language)).
If I were to program something with stringent performance constraints
today, I would try very hard to stay away from C++. Frankly, it is not
that I couldn't create abstractions in C++. The problem is that it just
takes so much more effort and code to do it, that it takes the fun out of
programming. This is one of the reasons why I decided to try other
application domains with less strict real-time requirements --- to be able
to use higher-level languages at work and to learn from the experience.
Compared to C++, SML makes it significantly easier to define and use
abstractions both in the small (perhaps especially in the small) and
in the large. Fortunately, there even exists a compiler that can
eliminate many kinds of abstraction penalty. So, unlike with many
other high-level languages, you can actually use such abstractions in
performance critical sections of your application. So, optimizations,
to me, are an enabling factor. Optimizations enable me to write code
as I want to write it. Not the other way around.
[...]
> Yes, I understand that I can define something like
> datatype ('a, 'b) function_with_docstring = FUN of ('a->'b) * string
> val double = FUN (fn x => 2*x, "A function doubling its argument")
> and even implement a mechanism to register all the docstrings
> and possibly other informations such as names and types, but
> frankly this sounds to me like a job for the language, not
> for the user.
I wouldn't recommend that approach. I would recommend using a tool
that extracts do***entation from the source code. In fact, I've been
working on such a tool myself, for my needs, but it is not yet at the
top of my task list. However, there already are tools that can
extract do***entation from SML source code. Here is one:
http://www.pllab.riec.tohoku.ac.jp/smlsharp/?SMLDoc
> As I have said before, it looks absurd to me that information which is
> available to the compiler (even if absence of a REPL the compiler knows
> the signatures, which is the things which are more im****tant for a
> do***entation tool, even in absence of docstrings) is hidden to the
> user.
Many SML implementations, even those without a REPL, provide means of
getting some of that information. MLton, for example, includes two
particularly useful switches, -show-def-use and -show-basis, for
getting such information. The -show-def-use switch prints out a
precise listing of all symbol definitions (including local symbols)
and uses within a program. The -show-basis option prints out all the
definitions ex****ted from a library or program.
> And what if I wanted to code an IDE for SML? It looks impossible
> without resorting to help from the underlying implementation.
Then, by all means, use such help! For example, I've written an Emacs
mode, http://mlton.org/EmacsDefUseMode,
for working with SML/MLton. It
uses information provided by MLton.
> > [...] In particular, it allows you to conveniently define new test
> > registration specifiers. [...]
Actually, although it used to, my unit test framework currently does not
really "register" tests, it just runs them immediately. It does collect
some minimal statistics on tests, however.
> How are implemented registers in SML? I would go with something like
> this
> val registerList = ref []
> fun register(name, function) =
> registerList := (name, function) :: ! registerList
Yes, that is one way to do it.
> or with a StringMap if I wanted to be able to retrieve functions by
> name. However, a register works with side effects and I am told that
> functional languages should avoid side effects as much as possible;
> nevertheless I don't see how to avoid mutating the reference here.
Avoiding side-effects isn't really just a language issue. It is a
program design issue. Generally speaking, it is beneficial to avoid
mutable state and side-effects in interfaces as much as possible. SML
has good sup****t for avoiding mutable state and side-effects. In
particular, unlike almost all imperative languages, it provides you
with a wide range of immutable data types, which means that you
basically never have to use mutable data in an interface unless that
is precisely what you want to do. In some cases, however, mutable
data structures and/or effectful interfaces can be very convenient,
because you are relieved from the task of explicitly passing all the
data, and mostly harmless. SML doesn't prevent you from using
mutation and effectful interfaces in such cases.
-Vesa Karvonen


|