Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Do we really need FPLs? We can do everything with the imperative languages.

22 views
Skip to first unread message

thelifter

unread,
May 15, 2002, 11:57:11 PM5/15/02
to
Hello,

in my last thread the answer from different people showed me that many
languages have higher order functions(HOF). For those that are new:
HOFs are functions that can receive other functions as parameters or
return them.

For example in Java I can do a HOF if I use an Interface that has only
one function. I then pass this interface as a paramter or return
value.

In C I have function pointers.

Since the only real advantage of FPL lies in the higher order
functions, where is this advantage now? It's gone. I can do everything
in other languages, maybe the syntax is a little more cumbersome.

To view it from another perspective: Java, C, C++ might also be
considered functional languages, so they provide all we need.

I think the real advantage of FPL and studies related to them is that
they teach us how to think differently about programming(like the
studies about combinator parsers). After we learned that, we can now
implement this lessons with most languages that we have, like Java, C,
C++, etc...
I think it's possible to write a combinator parser in Java. Just use
interfaces for the higher order functions.

Any comments are welcome.

Daniel C. Wang

unread,
May 16, 2002, 12:12:43 AM5/16/02
to
thel...@gmx.net (thelifter) writes:

> I think it's possible to write a combinator parser in Java. Just use
> interfaces for the higher order functions.
>
> Any comments are welcome.


Please, go and try to write combinator parsers in Java. I'll bet you'll
find it is a complete ugly mess. Then you will understand why Java isn't
a FPL.

William Lovas

unread,
May 16, 2002, 12:37:25 AM5/16/02
to
I'm interested to see what other people have to say. I have only a few
comments of my own..

In article <b295356a.0205...@posting.google.com>, thelifter wrote:
> For those that are new:
> HOFs are functions that can receive other functions as parameters or
> return them.
>
> For example in Java I can do a HOF if I use an Interface that has only
> one function. I then pass this interface as a paramter or return
> value.
>
> In C I have function pointers.

Wasn't the general concensus that C's function pointers aren't as useful,
since they're not implemented with closures?

Also, this may be the same thing (or very closely related), but with
function pointers, you lack the ability to create anonymous functions.
This sort of makes it difficult to uphold the "or return them" clause.
(You don't have this limitation with the Java interfaces you describe,
because Java allows you to construct anonymous classes on the fly.)

> Since the only real advantage of FPL lies in the higher order
> functions, where is this advantage now? It's gone.

Well, higher-order functions isn't the *only* advantage of functional
programming. Arguably, things like referential transparency are pretty
handy, for code optimization; Haskell programers would probably cite
lazy evaluation, too. There are others, i'm sure.

> I can do everything
> in other languages, maybe the syntax is a little more cumbersome.
>
> To view it from another perspective: Java, C, C++ might also be
> considered functional languages, so they provide all we need.

I don't think it's very instructional to look at things from the perspective
of "Oh, but i can do it here, just with uglier syntax!" After all, most
(all?) general purpose programming languages are Turing-complete -- would
you argue that since we can do everything we need to with assembly language,
all other languages are superfluous? I'd be surprised ;)

Really, the question is one of useful abstraction. Higher order functions,
when provided with a nice, clean syntax, are at least empirically a very
useful abstraction. The same goes for things like object-oriented style,
or modular programming -- these things are popular (i.e. implemented in
popular languages) because somebody thought they were a useful abstraction.
Certainly no one would argue that they are necessary to express everything
that's computable; rather, they make some important ideas *easier* to
express.

If programmers weren't concerned with ease of expression and meaningful
abstractions, the field of programming language research would be a pretty
mundane one.

cheers,
William

Peter Sestoft

unread,
May 16, 2002, 2:51:03 AM5/16/02
to

> > I think it's possible to write a combinator parser in Java. Just use
> > interfaces for the higher order functions.
> >
> > Any comments are welcome.
>
>
> Please, go and try to write combinator parsers in Java. I'll bet you'll
> find it is a complete ugly mess. Then you will understand why Java isn't
> a FPL.

I've implemented a parser combinator library in Generic C#, and indeed
what is pretty clear in a functional language looks extremely
scientific in an object-oriented one:

http://www.dina.kvl.dk/~sestoft/gcsharp/Parsers.cs

Enjoy!

Peter
--
Department of Mathematics and Physics * http://www.dina.kvl.dk/~sestoft/
Royal Veterinary and Agricultural University * Tel +45 3528 2334
Thorvaldsensvej 40, DK-1871 Frederiksberg C, Denmark * Fax +45 3528 2350

George Caswell

unread,
May 16, 2002, 2:43:22 AM5/16/02
to
On 15 May 2002, thelifter wrote:

> Hello,
>
> in my last thread the answer from different people showed me that many
> languages have higher order functions(HOF). For those that are new:
> HOFs are functions that can receive other functions as parameters or
> return them.
>
> For example in Java I can do a HOF if I use an Interface that has only
> one function. I then pass this interface as a paramter or return
> value.
>
> In C I have function pointers.
>

On the other hand, it's much more cumbersome to try and bundle data with
the function: In Scheme or Haskell there are short forms for currying - "map
f" in Haskell or (lambda (xs) (map f xs)) in Scheme to define in-line a
function of type [a]->[a]. In C the most direct way to do that would be to
define a new function: and since there are no anonymous functions in C that
would clutter the namespace unnecessarily, to say nothing of the headache of
actually storing that associated data somewhere, and deallocating it at the
appropriate time. C++ makes it moderately easier, at the expense of all
sanity and reason. I think the situation in Java is better.

> Since the only real advantage of FPL lies in the higher order
> functions, where is this advantage now? It's gone. I can do everything
> in other languages, maybe the syntax is a little more cumbersome.
>

First off, that is not true. The advantages of functional programming
languages (apart from the common traits most high-level languages share, like
garbage collection and such) have to do with the fact that they are designed
to support functional programming styles, just as the C family of languages is
designed to support imperative programming styles.

Second, your statement actually applies to many styles and languages. You
can do numeric work in languages other than Fortran. You can do text
manipulation in languages other than Perl or ELisp. You can do
object-oriented programming in C, or imperative programming in Haskell... You
can write parsers and compilers with languages not designed to write parsers
or compilers.. but do you really want to? That's the real question. People
write entire programs in C++, because it is The Language Which People Use -
it's quite sick. Almost all languages can be applied to any problem or
sub-problem, but it doesn't always work out too well. Sadly, people do it
anyway.

Third, note that that condition, that things you can do in functional
programming languages can also be done in C is trivially satisfied by the fact
that most functional programming languages are implemented in C. For that
matter, it could all be done in assembly.

> To view it from another perspective: Java, C, C++ might also be
> considered functional languages, so they provide all we need.
>

I don't agree. Certainly it's possible to use functional programming
paradigms when writing code in these languages: I find this to be quite
useful, in fact. However, the languages do not actively support the paradigm.
All of them require a lot of attention to *how* the problem is solved: The
steps that are taken, the order in which they're performed, how and where data
is stored, etc. There is no syntactic support for lazy evaluation, or
efficient (iterative) recursion. C and C++ don't even have adequate built-in
types: There is no built-in representation for a sequence of elements, for
instance. The built-in arrays almost provide this, except that you cannot
determine the size of a built-in array: at best you can find the size of the
buffer or variable in which it's stored - but if you pass the array to a
function that information is generally lost. C++ containers address this
problem, but interacting with the different container types generically is a
problem. Since there is no way outside of the classes to represent a
sequence, and the classes don't share a virtual interface, containers with
different implementation details are distinct types with no sensible
equivalence. Without memory management in C and C++ it's also difficult to
even express functions in functional form without invoking multiple copy
operations on the result data: More often pure functions returning large data
are defined in C as "void f(Input a, Result *b);", and either the caller must
concern themselves with creating the result data for modification by the "pure
function" or the function must include code to allocate the result data.

> I think the real advantage of FPL and studies related to them is that
> they teach us how to think differently about programming(like the
> studies about combinator parsers). After we learned that, we can now
> implement this lessons with most languages that we have, like Java, C,
> C++, etc...
> I think it's possible to write a combinator parser in Java. Just use
> interfaces for the higher order functions.
>
> Any comments are welcome.
>

I believe in choosing The Right Language For The Job. No language is
especially well-suited to every problem a programmer will face, but different
languages can be used together to make things happen in a sensible way. The
language we use to solve a problem has an effect on how we think about writing
the solution.

---GEC
New projects page: http://home.attbi.com/~sieg_haro/
(M-x depeche-mode)
"Must... Finish... Zaku..."

Matti Nykanen

unread,
May 16, 2002, 3:01:35 AM5/16/02
to
William Lovas <wlo...@force.stwing.upenn.edu> writes:

> In article <b295356a.0205...@posting.google.com>, thelifter wrote:
> > I can do everything
> > in other languages, maybe the syntax is a little more cumbersome.
> >
> > To view it from another perspective: Java, C, C++ might also be
> > considered functional languages, so they provide all we need.
>
> I don't think it's very instructional to look at things from the perspective
> of "Oh, but i can do it here, just with uglier syntax!" After all, most
> (all?) general purpose programming languages are Turing-complete -- would
> you argue that since we can do everything we need to with assembly language,
> all other languages are superfluous? I'd be surprised ;)
>
> Really, the question is one of useful abstraction. Higher order functions,
> when provided with a nice, clean syntax, are at least empirically a very
> useful abstraction. The same goes for things like object-oriented style,
> or modular programming -- these things are popular (i.e. implemented in
> popular languages) because somebody thought they were a useful abstraction.
> Certainly no one would argue that they are necessary to express everything
> that's computable; rather, they make some important ideas *easier* to
> express.

In Computer Science we often design languages for expressing the
knowledge at hand. Programming languages are designed for expressing
those computations we want ot perform. In designing such a language,
one fixes _basic_ concepts, to which everything else reduces.

In an object-oriented language, "everything is an object" - that is,
the object is the basic concept. In a functional language, the
function is the basic concept. You can do functional programming in an
object-oriented language: define the functional constructs as
shorthands for appropriate object constructs. And vice versa, objects
can be expressed as useful shorthands in a functional
language. However, shorthands tend to be more cumbersome to use than
"native" concepts.

Turing-completeness means that we can choose our basic concepts in
many different ways, and still express the same computations. (Or to
be more precise, get the same results, maybe via different
computations.)

However, programming languages are meant to be used for implementing a
software design. This is much easier when the basic concepts of the
language match the basic concepts of the design. If objects arise
naturally during design, then choose an object-oriented language; but
if you encounter things like streams, then choose a functional
language.

Often exactly the reverse happens: programmers know one approach, and
they try to fit the design to what they already know, rather than
using the design as a guide for finding out what more they should
learn to get the job done. It is very human - and very sensible - to
avoid extra work, but one must weigh carefully which is more work,
implementing a bad design or learning more to get it right.

If your design involves both, the pick either, and use shorthands for
the other. I think this is what starts language wars: is there a
reason to always choose one particular collection of basic concepts,
always relegating the other, less frequently needed concepts to
shorthands? Turing completeness does not solve this question one way
or the other: Turing completeness is about language semantics, this
question is about design pragmatics.

(One student project at this department looked fully object-oriented
at the outset: even the inputs were encodings of object
hierarchies. However, expressing searching these hierarchies proved
difficult with object-oriented concepts, so the team borrowed a
functional approach - still written with an object-oriented language -
for the search engine interface. It paid off.)

As written by a person whose ignorance of software engineering is
equalled only by his lack of knowledge on programming languages...

George Russell

unread,
May 16, 2002, 5:19:19 AM5/16/02
to
In principle I agree with thelifter. We do not really need functional languages. We
do not even really need languages; we could just program by punching machine-code onto
paper tape. Even if we were reduced to the latter extremity, functional languages would
still be useful in giving us a way of mentally structuring programs.

Jerzy Karczmarczuk

unread,
May 16, 2002, 5:45:25 AM5/16/02
to
George Russell wrote:
>
> In principle I agree with thelifter. We do not really need functional languages. We
> do not even really need languages; we could just program by punching machine-code onto
> paper tape.

A sarcasm is a sarcasm is a sarcasm.
Read and dismissed.

So, perhaps just to keep it a bit longer in your memory, I'll tell you a short
story which I told already once...

In old, glorious days, when Soviet Union passed from the Dark Ages to something
more gray (for scientist living there it was sunny and rose anyway), and they
started to built computers, to write papers on theory of computation, etc.,
they decided to promote the following philosophy:
"We have plenty of *really good* mathematicians.
We educate really *many* people in the engineering domains.
So, we can choose many of them to write the algorithms as efficiently as
possible, and have bunches of technicians to code them as efficiently,
economically etc., as possible.
All these divagations about "automatic" programming, higher level languages,
etc. are redundant, it is a waste of time. We need now to have the work
done *quickly*."

And so, they have consciously forsaken the research in the domain of programming
languages. There were exceptions, e.g. Markov, but in general nothing happened,
and the result of all that was as we know now, rather catastrophic.

===

The newsgroup devoted to graphic algorithm is dominated by young people (and
some
old dinosaurs). You'll find there periodically similar opinions about the
uselessness of abstract programming tools, some "gurus" *must* say from time to
time that all these "academic theories" are nonsense, that textbooks full of
math is a waste of paper, and a youngster with good knowledge of "C" and
assembler is much more productive than all those stupid theoreticians together.

I don't think there is any point in discussing with such people. Most of them
finally grow up. Unfortunately some percentage of them is a bunch of vultures
without any shame. What do I mean?
They finally read all this "formal and useless" material. They learn.
They use it for their coding. Very deeply hidden in their games and other
programs.
They continue to discourage other people from using high-level tools, in such
a way they weaken their potential competitors.

So, my only message to people who say that we don't really functional languages
is: Fine. Don't need them, don't use them. Go away, don't bother us.
Trying to convince people who don't want to be convinced is not a pedagogy, it
is a kind of indoctrination.

Jerzy Karczmarczuk

Pixel

unread,
May 16, 2002, 5:46:16 AM5/16/02
to
thel...@gmx.net (thelifter) writes:

> Since the only real advantage of FPL lies in the higher order
> functions, where is this advantage now? It's gone. I can do everything
> in other languages, maybe the syntax is a little more cumbersome.
>
> To view it from another perspective: Java, C, C++ might also be
> considered functional languages, so they provide all we need.

why you IMO won't do FP in those languages:

- lack of parametric polymorphism (C, Java)

in a typed language, the lack of parametric polymorphism is a big
problem. You won't get away using "void*" or "Object" because
functional programming imply a returned value. If it's "void*" or
"Object" you have to downcast back by hand.

- lack of garbage collector (C, C++)

functional programming inherently generates a lot of objects (since
new objects are created, contrary to modifying existing objects)

- explicit typing (C, C++, Java)

having to give the type of every little function is a no-no for
functional programming

- lack of closures (C, C++, Java)

cf to what other said

- lack of anonymous functions (C, C++, Java - inner classes which are
really ugly sugar)

i would also add that FP programming has changed since the advent of
type inference and lazyness. You can't program the same way in Scheme
you're programming in Haskell. I don't wanna introduce some racism in
functional languages, but IMO higher-order programming *needs* type
inference. For example Haskell is the only language I know where
programmers are using function composition (operator ".")

Joe Armstrong

unread,
May 16, 2002, 6:18:46 AM5/16/02
to
Pixel <pi...@mandrakesoft.com> writes:

... cut ...

> i would also add that FP programming has changed since the advent of
> type inference and lazyness. You can't program the same way in Scheme
> you're programming in Haskell. I don't wanna introduce some racism in
> functional languages, but IMO higher-order programming *needs* type
> inference. For example Haskell is the only language I know where
> programmers are using function composition (operator ".")

... and there I was doing proper higher order programming
every day in Erlang which is neither lazy nor has type inference :-)

/Joe

George Russell

unread,
May 16, 2002, 6:51:03 AM5/16/02
to
Jerzy Karczmarczuk wrote:
>
> George Russell wrote:
> >
> > In principle I agree with thelifter. We do not really need functional languages. We
> > do not even really need languages; we could just program by punching machine-code onto
> > paper tape.
>
> A sarcasm is a sarcasm is a sarcasm.
> Read and dismissed.
[long disagreement deleted]
Now what brought that on? I was not being sarcastic, I mean what I say. Although I write
mostly in Haskell, I could do everything by punching machine-code into paper tape. I do
not want to do that, because it would take c20 times as long (at the very least) to write
(or punch) code to do the same job. So I do not "really need" functional languages, even
though I think they are very useful.

Ketil Malde

unread,
May 16, 2002, 6:49:23 AM5/16/02
to
George Russell <g...@tzi.de> writes:

> So I do not "really need" functional languages, even though I think
> they are very useful.

FPLs are redundant in the sense cars are redundant - we can do
everthing we use cars for with hand-carts and bicycles.

-kzm
--
If I haven't seen further, it is by standing in the footprints of giants

Joachim Durchholz

unread,
May 16, 2002, 7:15:03 AM5/16/02
to
Joe Armstrong wrote:
> ... and there I was doing proper higher order programming
> every day in Erlang which is neither lazy nor has type inference :-)

How do you keep track of the various levels of higher-orderness in your
programs?

Regards,
Joachim
--
This is not an official statement from my employer.

Lauri Alanko

unread,
May 16, 2002, 9:08:51 AM5/16/02
to
In article <uvg9op...@agere.com>,

Daniel C. Wang <nospam...@agere.com> wrote:
>Please, go and try to write combinator parsers in Java. I'll bet you'll
>find it is a complete ugly mess. Then you will understand why Java isn't
>a FPL.

It's been done, so there's no need to speculate, one can see for oneself:

http://citeseer.nj.nec.com/dijkstra01lazy.html


Lauri Alanko
l...@iki.fi

Daniel C. Wang

unread,
May 16, 2002, 9:39:22 AM5/16/02
to

Lauri Alanko <l...@iki.fi> writes:

A wonderful URL to remember. To bad I didn't know about this when ranting
about silly one-line quicksort examples. This should be in a FAQ.

Ulf Wiger

unread,
May 16, 2002, 10:49:35 AM5/16/02
to
>>>>> "-" == thelifter <thel...@gmx.net> writes:

-> Since the only real advantage of FPL lies in the higher order
-> functions, where is this advantage now? It's gone. I can do
-> everything in other languages, maybe the syntax is a little more
-> cumbersome.

-> To view it from another perspective: Java, C, C++ might also be
-> considered functional languages, so they provide all we need.

This reminds me of some actual conversations I've had with people
who insist that Excel is the only office program you need.
You can use it for word processing, databases, presentations... oh,
and spreadsheets too. So why bother with another program?

(This analogy may even be effective on managers, who are known to
master both Excel and e.g. PowerPoint -- most significant systems
design nowadays is done in PowerPoint, in case you didn't know.)

/Uffe (this post written in Emacs - not Excel, although I'm sure it
could be done somehow)
--
Ulf Wiger, Senior Specialist,
/ / / Architecture & Design of Carrier-Class Software
/ / / Strategic Product & System Management
/ / / Ericsson Telecom AB, ATM Multiservice Networks

Christopher Browne

unread,
May 16, 2002, 12:57:23 PM5/16/02
to
The world rejoiced as Ulf Wiger <Ulf....@ericsson-cut-to-reply.com> wrote:
>>>>>> "-" == thelifter <thel...@gmx.net> writes:
>
> -> Since the only real advantage of FPL lies in the higher order
> -> functions, where is this advantage now? It's gone. I can do
> -> everything in other languages, maybe the syntax is a little more
> -> cumbersome.
>
> -> To view it from another perspective: Java, C, C++ might also be
> -> considered functional languages, so they provide all we need.
>
> This reminds me of some actual conversations I've had with people
> who insist that Excel is the only office program you need.
> You can use it for word processing, databases, presentations... oh,
> and spreadsheets too. So why bother with another program?

Lotus used to sell a program called Symphony, which quite formally
took this approach to things.

It mapped _everything_ onto spreadsheets.

- Word processing was done with a front end processor that put all the
text into the leftmost cell.

- The "database form" scheme worked by defining a range of a
spreadsheet for the data, and then having a FEP that would map this
to a form.

- I lied. The "terminal software" didn't really use the spreadsheet
terribly much. But I doubt many people used that instead of Telix...
--
(concatenate 'string "cbbrowne" "@acm.org")
http://www.cbbrowne.com/info/spreadsheets.html
Incrementally extended heuristic algorithms tend inexorably toward the
incomprehensible.

Tom Thomson

unread,
May 16, 2002, 1:23:33 PM5/16/02
to
On 15 May 2002 20:57:11 -0700, thel...@gmx.net (thelifter) wrote:

>Hello,
>
>in my last thread the answer from different people showed me that many
>languages have higher order functions(HOF). For those that are new:
>HOFs are functions that can receive other functions as parameters or
>return them.
>
>For example in Java I can do a HOF if I use an Interface that has only
>one function. I then pass this interface as a paramter or return
>value.
>
>In C I have function pointers.
>
>Since the only real advantage of FPL lies in the higher order
>functions, where is this advantage now? It's gone. I can do everything
>in other languages, maybe the syntax is a little more cumbersome.
>
>To view it from another perspective: Java, C, C++ might also be
>considered functional languages, so they provide all we need.

Alos for example in binary machine code I can do a HOF if I pointer. I


then pass this interface as a paramter or return value.

In binary machine code I have pointers, so I have function pointers.

Since the only real advantage of FPL lies in the higher order
functions, where is this advantage now? It's gone. I can do everything
in other languages, maybe the syntax is a little more cumbersome.

To view it from another perspective: binary machine code might also be
considered a functional language, so it provides all we need.

All these complex languages like Haskell and Java and C++ and C and
symbolic assembly languages are unneccessary. Binary machine code
allows us to do it all.

Of course we don't even need binary machine code, because we don't
need stored programme computers. Anything a computer can do can also
be done by some people with simple tools like slide rules and Fuller's
rules.

And those tools are superfluous too: Some people with nothing but
pencils and a lot of paper can do it all - even slide rules and
Fuller's rules are unnecessary complications of simple computation.


Albert Lai

unread,
May 16, 2002, 7:08:13 PM5/16/02
to
thel...@gmx.net (thelifter) writes:

> Since the only real advantage of FPL lies in the higher order
> functions, where is this advantage now? It's gone. I can do everything
> in other languages, maybe the syntax is a little more cumbersome.

I hate cumbersome syntax. It doesn't scale. It is also an
anti-thesis to software engineering --- you can't maintain my code if
you have to spend a long time to decipher my intention, which happens
if I mimic objects in Haskell, or functions in Java, or abstract data
types in Fortran.

Also notice that we can equally say: Do we really need imperative
languages? We can do everything with functional languages.

If I had to choose one between your subject line and the above
statement (fortunately I don't have to), I would choose the latter.
Personal taste.

Albert Lai

unread,
May 16, 2002, 7:22:58 PM5/16/02
to
mich...@btinternet.com (Tom Thomson) writes:

> Of course we don't even need binary machine code, because we don't
> need stored programme computers.

Haha! My continuation of this is "all we need is a lambda expression
evaluator, or alternatively a spineless tagless G-machine! And both
can be reduced to just a bunch of people using pencil and paper, a lot
of paper."

And who needs decimals? We have Church numerals...

Ulrich Hobelmann

unread,
May 17, 2002, 3:52:28 AM5/17/02
to
Well, you don't need those languages, as they only provide an
abstraction layer above assembly language,
but still this abstraction lets you focus on the really
important things, and just dump the rest (let the compiler do it).

Hand-coding C/asm is mostly a matter of efficiency.

To me the main use of higher-level languages is in their nice
lexical scoping (unlike C), anonymous functions, _strong_ compile
time typing (unlike Java), and short, but expressive syntax.

If you don't need all of this, just don't use it, but don't
complain if you need more time for coding stuff ;)

Regards, Ulrich

PS: of course functional languages (in the ML-sense, not Haskell)
are just imperative languages, as they have side effects.

Alan Baljeu

unread,
May 17, 2002, 6:19:29 PM5/17/02
to

"Albert Lai" <tre...@vex.net> wrote in message
news:4uofffu...@vex.net...

>
> I hate cumbersome syntax. It doesn't scale. It is also an
> anti-thesis to software engineering --- you can't maintain my code if
> you have to spend a long time to decipher my intention, which happens
> if I mimic objects in Haskell, or functions in Java, or abstract data
> types in Fortran.
What do you mean by anti-thesis? Are you saying that cumbersome syntax
prevents effective software engineering? I would think this is too
strong of a statement. There are ways to deal with bad syntax.
Inconsistent semantics however, there's an obstacle to effective
engineering.

Alan

Albert Lai

unread,
May 18, 2002, 12:43:33 AM5/18/02
to
"Alan Baljeu" <aba...@sympatico.ca> writes:

> What do you mean by anti-thesis? Are you saying that cumbersome syntax
> prevents effective software engineering? I would think this is too
> strong of a statement.

Yes, that is what I am saying. What is wrong with a strong statement?

> There are ways to deal with bad syntax.

Take for example I pull a hack of using several static arrays to mimic
a heap, and integers within the range of array indexes to mimic
pointers, so that I can pretend I have non-linear data structures such
as lists, trees, and graphs. In Fortran.

If I just do this for 10 lines of code in an otherwise straightforward
1000-line program, that's of course fine. But that is a toy. The
reality is I probably have to do it all over the place in a 10000-line
module. It will become unreadable. Oh, of course, that is "not
true": I wrote it "therefore" I can read it; I "just" have to spend
two hours explaining my array hack to my peer, then he can read it;
(repeat for every peer for every two weeks because humans are
imperfect); and I "just" have to add tons of comments so that six
months later I "just" have to spend two hours digesting the comments
in order to remind myself how to read it and maintain it. Yeah right.

If that is not bad enough a wound, let me add salt. To make this
array hack work consistently, there are maybe 10 data invariants I
have to preserve throughout the whole module. Now let's maintain the
code: let's modify some features, say add an array because the boss
wants me to store employee id's in addition to employee names, or some
other perturbations. Now tell me, bearing in mind I am an imperfect
human, what is the chance that I carry out the perturbation without
violating any of the 10 data invariants? Oh, of course, I "just" have
to pay attention to the comments I wrote, and I "just" have to "debug"
it if a problem is observed --- nevermind that the whole system
consists of several modules, someone "just" knows that my module is
the cause of the problem. Yeah right.

Now there are ways to deal with it. Firstly, add a macro language to
Fortran so that I write in terms of records and pointers and they get
consistently translated to arrays and indexes. Secondly, switch to
Pascal or C so I can write in terms of records and pointers. Well, I
think I call these "getting rid of" rather than "dealing with".

Now if you have ways of dealing with them while keeping them and
enabling effective software engineering, I have some questions on
their success. Why are they not taught to future software engineers?
Why do software engineers add features to languages or even switch to
other languages entirely once in a while?

legere

unread,
May 18, 2002, 9:27:16 PM5/18/02
to
Great quote from Peter's program:
"A cool feature of OOP is that the simplest examples are 500 lines."


One wonders what the motivation to write this was :)


Peter Sestoft <ses...@ellemose.dina.kvl.dk> wrote in message

Joachim Durchholz

unread,
May 19, 2002, 5:19:25 AM5/19/02
to
legere wrote:
> Great quote from Peter's program:
> "A cool feature of OOP is that the simplest examples are 500 lines."
>
> One wonders what the motivation to write this was :)

I don't know what Peter's OO experience is, but the examples written in
Eiffel are about 50-100 lines long. Well, the SIMPLEST examples are
about 10 lines, but, of course, the simpler, the less they demonstrate.

The verbosity of C++ and Java just leads many people to the conclusion
that all OO languages must be verbose. (BTW Eiffel is Pascal-style, i.e.
verbose at the lexical level. It's quite terse at the semantic level,
i.e. there are a lot of things that it will do for you where a C++
programmer must write constructors, helper classes, or other redundant
stuff.)

jade_mei

unread,
May 20, 2002, 1:55:45 AM5/20/02
to
"Alan Baljeu" <aba...@sympatico.ca> wrote in message news:<smfF8.20777$t2.29...@news20.bellglobal.com>...

hi

jade_mei

unread,
May 20, 2002, 1:58:24 AM5/20/02
to
"Ketil Malde" <ket...@ii.uib.no> wrote in message news:<eg3cwsn...@sefirot.ii.uib.no>...

> George Russell <g...@tzi.de> writes:
>
> > So I do not "really need" functional languages, even though I think
> > they are very useful.
>
> FPLs are redundant in the sense cars are redundant - we can do
> everthing we use cars for with hand-carts and bicycles.
>
> -kzm

safhkasfgjqwe;

jade_mei

unread,
May 20, 2002, 1:59:32 AM5/20/02
to
"Ketil Malde" <ket...@ii.uib.no> wrote in message news:<eg3cwsn...@sefirot.ii.uib.no>...
> George Russell <g...@tzi.de> writes:
>
> > So I do not "really need" functional languages, even though I think
> > they are very useful.
>
> FPLs are redundant in the sense cars are redundant - we can do
> everthing we use cars for with hand-carts and bicycles.
>
> -kzm

qqqqqq
eeewq

Alan Baljeu

unread,
May 21, 2002, 10:18:59 PM5/21/02
to

"Albert Lai" <tre...@vex.net> wrote in message
news:4uk7q2k...@vex.net...

> "Alan Baljeu" <aba...@sympatico.ca> writes:
>
> > What do you mean by anti-thesis? Are you saying that cumbersome syntax
> > prevents effective software engineering? I would think this is too
> > strong of a statement.
>
> Yes, that is what I am saying. What is wrong with a strong statement?
There's nothing wrong with a strong statement in general. I only mean this
statement is stronger than I believe is true. I might say cumbersome syntax
makes effective software engineering harder, and I would say poor semantics
makes it nearly impossible.

>
> > There are ways to deal with bad syntax.
>
> Take for example I pull a hack of using several static arrays to mimic
> a heap, and integers within the range of array indexes to mimic
> pointers, so that I can pretend I have non-linear data structures such
> as lists, trees, and graphs. In Fortran.
>
> If I just do this for 10 lines of code in an otherwise straightforward
> 1000-line program, that's of course fine. But that is a toy. The
> reality is I probably have to do it all over the place in a 10000-line
> module. It will become unreadable. Oh, of course, that is "not
> true": I wrote it "therefore" I can read it; I "just" have to spend
> two hours explaining my array hack to my peer, then he can read it;
> (repeat for every peer for every two weeks because humans are
> imperfect); and I "just" have to add tons of comments so that six
> months later I "just" have to spend two hours digesting the comments
> in order to remind myself how to read it and maintain it. Yeah right.
>
You cite as example using arrays in Fortran to imitate records and
pointers. This could be considered poor syntax, but I think the main
problem is poor semantics.

I'm not knowledgeable about Fortran by any means, but if it is lacking
in these features, this is not something syntax can fix. What we need
are records or a means to implement records. Once we have that, the
solution is just a matter of creating an abstraction. If you can create
an abstraction, the engineering problem is solvable.

E.g., if we were allowed a non-homogeneous array, we could define constants:
EMPNAME = 1
SALARY = 2
DEPARTMENT = 3
an array:
record = array of 3 elements
and now:
record[EMPNAME] = "John"
record[SALARY] = 75000.00
record[DEPARTMENT] = DEP_ACCOUNTING

If you have invariants you need to maintain, you simply write functions to
access the record, and tell people not to directly access the array. With
these you could more easily control and monitor how coding is done, but even
without that, a simple protocol can easily be established to keep the code
correct. Write a hand full of functions which are allowed to access the
record
type, and forbid all other attempts to work with that data of that type.
You can do this even if type checking is not supported in the language.

(Thought: a crude form of runtime invariant-checking would be to extend the
record with a checksum element, and make all the priveledged functions
maintain
the checksum. Any invalid modifiers would corrupt it and be caught in the
testing
process.)

We could do better if we had other additional semantics, like dynamic
allocation,
information hiding, type definition and so on, but I figure if you have the
ability
to create a record, to allocate memory, to define a function, and to pass
information
around, you can engineer the software, REGARDLESS of the syntax. (But I'm
not
counting the extreme cases.)

What will hinder proper engineering is if the language semantics are
inconsistent,
confusing, or mutable. If someone else can write code which changes how
existing
code operates - this is a problem. If you want to write a generic function,
but
the language treats integers or arrays or strings different from user
structs, this
is a problem. Any time a language says you can do it this way, except...,
that's the
problem.

The advantages of Haskell over C++ and Fortran are two: consistent
semantics and more
advanced semantics. You can create better abstractions in Haskell, and your
job
is thus easier. Nothing _prevents_ using straight C to engineer a solution
using
just struct and function definitions. It just takes longer.

Alan

thelifter

unread,
May 26, 2002, 1:46:48 AM5/26/02
to
Ulf Wiger <Ulf....@ericsson-cut-to-reply.com> wrote in message news:<xczbsbg...@etxb.ericsson.se>...

> This reminds me of some actual conversations I've had with people
> who insist that Excel is the only office program you need.
> You can use it for word processing, databases, presentations... oh,
> and spreadsheets too. So why bother with another program?

Now come on, lets be honest here. People like putting things to an
extreme in order to invalidate an argument.

I will put my opinion in a different way:

As was shown even many languages that are considered Non-FPL, like
C,C++ and Java can implement many or all of FPL features like Higher
order functions.

Now the big question:

How much harder would it be to solve a problem using a Non-FPL instead
of a FPL.

a) If the answer is: basically you can solve it the same way, you only
will have a more verbose syntax.
Then I would say, well, I do not need to learn a FPL(although doing so
might be beneficial).

b) On the other hand if the answer is: You can't solve it the same way
because you don't have certain features, or the solution is MUCH more
verbose or MUCH more complicated.

In this case I would have a strong case for a the FPL...

From what I know a) is the case in reality, please prove me wrong
trough an example. I looked at the examples at that page for the
programming languages comparison(
http://www.bagley.org/~doug/shootout/) and didn't find one where the
functional approach had a significant advantage...

Just my 0.05$

Joachim Durchholz

unread,
May 26, 2002, 1:47:53 PM5/26/02
to
thelifter wrote:
>
> As was shown even many languages that are considered Non-FPL, like
> C,C++ and Java can implement many or all of FPL features like Higher
> order functions.

Higher-order functions are NOT the main feature of a functional
language. The ability to have HOFs is a necessary but not a sufficient
condition for functional programming.
Identifying functional programming as "something that uses functions" is
just as misleading as identifying object-oriented programming as
"something that uses objects" (the core of OO is run-time polymorphism,
objects are just the standard vehicle to support this style of
programming - just as functions are the standard vehicle to support
functional programming).

> Now the big question:
>
> How much harder would it be to solve a problem using a Non-FPL instead
> of a FPL.
>
> a) If the answer is: basically you can solve it the same way, you only
> will have a more verbose syntax.
> Then I would say, well, I do not need to learn a FPL(although doing so
> might be beneficial).

Syntax does matter.
I'm using an OO language that has no class methods. It's possible to
emulate them, but I need to create extra classes, which have to go into
separate files, which makes it difficult to find all the code that's
relevant for the class. For a mere syntactic inconvenience, it's
hampering my productivity enormously.
Similarly, it's easy to construct closures in an OO language. All you
have to do is to create a new class per closure type you want to
construct. But: you have to name the class, put it into a file, remember
to change it whenever the function in question is changed, and do all
the other boring and pointless tasks that pop up once you start
separating stuff that belongs together. Again, a mere syntactic
inconvenience, but the inconvenience is to large that it's unwise to try
to graft functional style onto any pure OO language.
Actually it would be easy to construct closures in Smalltalk, but it's
not done very often, even though the syntactic inconvenience is minimal.
Maybe this all is the background why currying is considered so
important. Currying is a very limited form of constructing closures (you
can close off just the last arguments of a function, if you want to do
that with an indermediate argument, you have to write a lambda
expression), yet most functional code that I have seen works happily
with currying and not much else. (Disclaimer: I haven't seen too much
functional code in my life. Take this as the views of somebody with more
theory than practice unter his belt, and with the appropriate spoonful
of salt.)

> From what I know a) is the case in reality, please prove me wrong
> trough an example. I looked at the examples at that page for the
> programming languages comparison(
> http://www.bagley.org/~doug/shootout/) and didn't find one where the
> functional approach had a significant advantage...

That's not a surprise: the shootout is limited to problems that can be
solved in any language.

There was once an experiment conducted at a military installation (a
branch of the US Navy IIRC). A trajectory tracking system (again, IIRC),
to be implemented by different programmers in different languages. There
were a dozen or so entries, in various languages; some languages were
used by more than one participant, and some participants entered with
more than one program. In all, it was a handful over a dozen entries,
ranging from 200 lines (Haskell) to 1000+ (C, I think).
Actually Haskell came out best, not only because it was the smallest
program written in the shortest time, it was also explicitly commended
because the code was considered easiest to follow (so much for the idea
of "functional languages are conceptually difficult" - maybe writing
them is conceptually difficult, but reading obviously isn't, since the
judges didn't have a functional background (well, at least they didn't
admit it - maybe there was a cabal of functionalists at work)).
They still didn't recommend Haskell, for various reasons (mainly because
Haskell isn't mainstream - the usual chicken-and-egg problem).

If anybody can dig up the URL of the report, I'd be glad to see it
posted. I read it more than a year ago, but I forgot where I found it.

Pascal_Grossé

unread,
May 26, 2002, 5:02:54 PM5/26/02
to
Dans l'article <3CF11FC9...@gmx.de>, Joachim Durchholz a écrit :

> There was once an experiment conducted at a military installation (a
> branch of the US Navy IIRC). A trajectory tracking system (again, IIRC),
> to be implemented by different programmers in different languages. There
> were a dozen or so entries, in various languages; some languages were
> used by more than one participant, and some participants entered with
> more than one program. In all, it was a handful over a dozen entries,
> ranging from 200 lines (Haskell) to 1000+ (C, I think).
> Actually Haskell came out best, not only because it was the smallest
> program written in the shortest time, it was also explicitly commended
> because the code was considered easiest to follow (so much for the idea
> of "functional languages are conceptually difficult" - maybe writing
> them is conceptually difficult, but reading obviously isn't, since the
> judges didn't have a functional background (well, at least they didn't
> admit it - maybe there was a cabal of functionalists at work)).
> They still didn't recommend Haskell, for various reasons (mainly because
> Haskell isn't mainstream - the usual chicken-and-egg problem).
>
> If anybody can dig up the URL of the report, I'd be glad to see it
> posted. I read it more than a year ago, but I forgot where I found it.
>

It can be found at www.haskell.org:

http://www.haskell.org/practive.html

under the name 'Haskell vs. Ada vs. C++ vs. Awk vs. ..., An experiment in
software prototyping productivity', by Paul Hudak and Mark P.Jones, 16 pages.

Pascal

Joachim Durchholz

unread,
May 26, 2002, 5:33:27 PM5/26/02
to

Tomasz Zielonka

unread,
May 26, 2002, 5:56:30 PM5/26/02
to
Joachim Durchholz napisał:

>
> If anybody can dig up the URL of the report, I'd be glad to see it
> posted. I read it more than a year ago, but I forgot where I found it.

http://www.haskell.org/papers/NSWC/jfp.ps ?

regards,
tom

--
.signature: Too many levels of symbolic links

Andreas Rossberg

unread,
May 27, 2002, 5:47:21 AM5/27/02
to
thelifter wrote:
>
> As was shown even many languages that are considered Non-FPL, like
> C,C++ and Java can implement many or all of FPL features like Higher
> order functions.
>
> Now the big question:
>
> How much harder would it be to solve a problem using a Non-FPL instead
> of a FPL.

As other people already pointed out, higher-order programming is
virtually impossible without

1. garbage collection (to manage closures)

So vanilla C, C++, Pascal, etc. are definitely out. Moreover, SERIOUS
higher-order programming IMO strongly requires

2. functional syntax (anonymous functions, currying)
3. a decent type system (polymorphism, type inference)

All "modern" imperative/OO languages still fall short wrt. at least one
of these additional properties.

Lack of all these features makes it hard enough to express common FP
idioms in conventional languages such that, I guess, no sane FP
programmer would actually bother trying (except, maybe, for illustrating
this very point).

--
Andreas Rossberg, ross...@ps.uni-sb.de

"Computer games don't affect kids; I mean if Pac Man affected us
as kids, we would all be running around in darkened rooms, munching
magic pills, and listening to repetitive electronic music."
- Kristian Wilson, Nintendo Inc.

Peter Sestoft

unread,
May 30, 2002, 3:30:59 AM5/30/02
to

> Great quote from Peter's program:
> "A cool feature of OOP is that the simplest examples are 500 lines."
>
> One wonders what the motivation to write this was :)

Well, I'm repeatedly surprised how verbose mainstream programming
languages are (so I was imprecise saying `OOP' above; no doubt there
are OO languages with more economy of expression, as pointed out by
Joachim Durchholz).

<aside>
Also, how hard it is for mainstream languages to convey much
information, or compile-time guarantees, using all those long words.

For instance, in Java I may write, in a field declaration:

class C {
private static final int[] arr = ...;
...
}

yet if the array reference is handed to somebody outside class C, any
element of arr can be changed by anybody at any time; arr[2] + arr[2]
need no longer evaluate to an even number.
</aside>

Now, is the verbosity of mainstream languages a bad thing?

* Yes: It makes it harder to present meaningful concrete code
examples in lectures, or to make a point in a discussion in front
of a whiteboard.

For this reason I much prefer using Standard ML as a communication
and implementation language in a compiler course: (1) the
essentials of a compiler or interpreter can be shown in one slide;
and (2) types etc in the code actually provide guarantees, in
contrast to the prevailing OOP runtime casts needed when using
general data structures. Generics in C# and Java will help a lot
on the latter point (at the cost of a little more verbosity).

Also, it's probably easier to understand a 6 page program module
than a 30 page program module: you can have everything in front of
you at the desk.

* No: In Java and C# I feel very productive, producing page after
page of neat code. And feeling productive is good for you.

Also, typing all those keywords etc leaves your brain free to think
about the program, so maybe you discover more design mistakes early
simply because the process takes longer? Well, maybe not. But it
reminds me of Don Knuth saying (in an interview with the excellent
German magazine c't) that he prefers writing his book drafts in
longhand, because on the computer he types too fast for his
thinking to catch up.

Peter
--
Department of Mathematics and Physics * http://www.dina.kvl.dk/~sestoft/
Royal Veterinary and Agricultural University * Tel +45 3528 2334
Thorvaldsensvej 40, DK-1871 Frederiksberg C, Denmark * Fax +45 3528 2350

Markus Mottl h9303956@wu-wien.ac.at

unread,
May 30, 2002, 7:00:41 AM5/30/02
to
Peter Sestoft <ses...@ellemose.dina.kvl.dk> wrote:
> * No: In Java and C# I feel very productive, producing page after
> page of neat code. And feeling productive is good for you.

I also think that this peculiar form of self-deception is a major reason
why people don't want to shift to more expressive languages.

Regards,
Markus Mottl

--
Markus Mottl mar...@oefai.at
Austrian Research Institute
for Artificial Intelligence http://www.oefai.at/~markus

Joachim Durchholz

unread,
May 30, 2002, 4:23:35 PM5/30/02
to
Markus Mottl h930...@wu-wien.ac.at wrote:
> Peter Sestoft <ses...@ellemose.dina.kvl.dk> wrote:
>
>> * No: In Java and C# I feel very productive, producing page after
>> page of neat code. And feeling productive is good for you.
>
> I also think that this peculiar form of self-deception is a major reason
> why people don't want to shift to more expressive languages.

I have seen this taken to extremes. People programming in RPG (which
combines the disadvantages of Basic and assembly language) and feeling
immensely productive.
Programming in that context meant writing the same code over and over,
with variations. After a few weeks, people became "productive" because
they had absorbed the standard patterns and could write them down almost
without thinking.
The man responsible for the approach left the team after six years, half
a year before the legacy problems started to become serious...

Steven Shaw

unread,
Jun 6, 2002, 10:37:22 AM6/6/02
to
This is an excellent question!

I'd like to add a couple of other points for the functional programming
gurus to address. My comments refer to sentiment from this thread and others
like it.

"FPL are great because they allow you to do all sorts of interesting
compiler optimisations". Why then, don't FPL outperform other languages at
the language shootout?

OCaml seems to perform well on the shootout. Is the code using imperative
features

Often cited as an argument for FPLs is the quicksort example which is quite
concise in, say, Haskell. Is it possible for a compiler to compile this
example into native code that compares with the speed of C?

I've seen the comment that dynamic dispatch is OOPLs greatest thing. I
agree. Is is possible to do dynamic dispatch a FPL?

Just because some popular imperative/oo languages don't have parametric
polymorphism, type inference or garbage collection doesn't mean that these
kinds of languages can't have these features. For example, I noticed
recently, that Modula-3 has these but a limited form of type inference (and
still supports systems programming!).

What are FPLs best at? Is it compilers?

Can we have our cake and eat it too? I like the idea of OCaml -
oo/imperative and functional language combined. Is this the
way-of-the-future for programming languages? Consider this senario:
* a problem lends itself to FPL
* you implement in FPL
* your program is a success and many new requirements are made
* the problem doesn't lend itself so much to FPL any more
* do you: rewrite in OOPL or write new bits in OOPL and interface to
FPL? Just resign?


Simon Helsen

unread,
Jun 6, 2002, 12:21:49 PM6/6/02
to
Hi,

>This is an excellent question!

I don't think so. As been addressed in this thread, it is about what we
can do more concise, more readable, more abstract. FPLs are generally
advanced languages and the absence of a default imperative style makes
programs more reliable and more correct (this is not a proven statement,
just one that builds on experience and exemplary evidence, and perhaps
also the lack of counter-evidence).

To understand the power of FPLs, you have to see a simple, nice and
powerful example. Look at the following code:
http://www.informatik.uni-freiburg.de/~thiemann/haskell/WASH/UseCGI4.hs

this is a CGI-script written in Haskell with the use of the WASH-library
(http://www.informatik.uni-freiburg.de/~thiemann/haskell/WASH)

To see what it does, run the program here:
http://nakalele.informatik.uni-freiburg.de/cgi/WASH/UseCGI4.cgi

Now, what makes this little elegant and powerful program what it is? Let
me say, type classes, monads (algebraic datatypes), purity (I mean, no
side-effects, except of course within the monad) and IMO, the use of
higher-order functions (= first class functions = closures) -both within
the script and the library.

Let us examine some parts of the code: since this script generates HTML,
you'll find stuff like "standardPage", which makes a typical HTML page, it
is defined as follows

html (head (title (text ttl))
## body (h1 (text ttl) ## elems))

html, head, title etc. are element "transformers", written in a strongly
typed functional style. (for details, I refer to the actual library and
papers)

the functions showIt and (computation "addition" "+" (+)) are both of
functional type and passed around (as argument of submit e.g.). The same
applies to (+), which is a primitive function. This is all higher-order
function programming.

This actually happens so often (not just in WASH-code) that FP-programmers
don't think about it anymore! A function is treated like another value and
this adds the abstraction possibilities for the programmer dramatically.

Simply try to do something like WASH (and this little script) in any
imperative language that does not provide HOFs, type classes, algebraic
datatypes. You wouldn't even start doing it (I'm not saying it is
impossible - a Turing machine does the job, it is all just a matter of how
elegant, simple and general it works. Imperative languages are simply
outdated by the facilities of FPs.

Of course, FPs have lots of other problems (syntax may be one, but there
are quite a bunch of other more down-to-earth problems that have nothing
to do with the power and quality of the languages!)

>"FPL are great because they allow you to do all sorts of interesting
>compiler optimisations". Why then, don't FPL outperform other languages at
>the language shootout?

? FPLs are more abstract. How do you want to compare that. The
optimisations of a functional compiler try to generate code as efficient
as possible, rivaling C. *However* it is safe and abstract, so why use C?

>OCaml seems to perform well on the shootout. Is the code using imperative
>features

don't know.

>I've seen the comment that dynamic dispatch is OOPLs greatest thing. I
>agree. Is is possible to do dynamic dispatch a FPL?

dynamic dispatch? Great? Dynamic dispatch is bound to inheritence. As
every (even OO) software designer will tell you: inheritence is dangerous
and should be avoided as much as possible (because it breaks encapsulation
-> read the COM-model of MS as an example). Dynamic dispatch? Why?

>Just because some popular imperative/oo languages don't have parametric
>polymorphism, type inference or garbage collection doesn't mean that these
>kinds of languages can't have these features. For example, I noticed
>recently, that Modula-3 has these but a limited form of type inference (and
>still supports systems programming!).

Modula-3 is age-old and died out. Although it is indeed a cool language,
it IMO surpassed by modern FPLs. Of course, you can add great features to
imperative languages, but it hardly happens! (Smalltalk is an exception in
so far that it has HOFs, but it is dynamically typed)

>What are FPLs best at? Is it compilers?

might be. Strongly typed FPLs tend to be superb for program
transformations.

Kind regards,

Simon

Victor B. Putz

unread,
Jun 6, 2002, 1:17:23 PM6/6/02
to

It seems like in many of these cases there is a fair amount of attention
on executable performance (cpu/memory usage). That's what Bagley's
shootout focuses on, for example.

The problem is, benchmarks focus on these things because they are easy
to measure. The problem is that in the long term they don't matter.
I'm not saying that they're totally unimportant, but if you're talking
about the long-term cost of a piece of software, in many cases final
executable speed is not nearly as important as lifecycle costs. After
all, if you want software to run twice as fast, you can generally buy a
machine that's twice as fast. If you want software to be *developed*
twice as fast, it will probably cost you a lot more to buy a developer
that's twice as fast--and may not even be possible!

Unfortunately, it's a sticking point; the only way to get good metrics
is to measure functionally identical large projects over their entire
lifecycle implemented in various languages... which isn't possible.

But we should really spend more time looking at defects
injected/detected during development, cost of maintenance of an existing
piece of software, total time spent in development, that sort of thing.
For example, I did a PSP study in my MS program comparing identical
programs written in C++ and Eiffel, and found that in this ridiculously
small sample size, Eiffel outperformed C++ in defects injected, defects
found earlier in the lifecycle, etc.,--which meant that *FOR ME*, Eiffel
was a better choice for that sort of problem. (I recieved a lot of
criticism from C++ folks for that one, but it was a personal study not
intended to necessarily reflect the developer population.)

THAT is where the gains are made by transitioning to functional
languages. Correct behaviour early is what we want more of the time
than the fastest executable possible.

-->VPutz

Victor B. Putz

unread,
Jun 6, 2002, 1:26:03 PM6/6/02
to
Simon Helsen <hel...@informatik.uni-freiburg.de> writes:

> dynamic dispatch? Great? Dynamic dispatch is bound to inheritence. As
> every (even OO) software designer will tell you: inheritence is dangerous
> and should be avoided as much as possible (because it breaks encapsulation
> -> read the COM-model of MS as an example). Dynamic dispatch? Why?

Hmm... I'm not certain that I agree with the assertion that "every (even
OO) software designer will tell you: inheritance is dangerous".

I'm a software designer (even OO!), and I don't think inheritance is
dangerous. I even think that MULTIPLE inheritance is OK if used
correctly in a language environment that supports it well (Eiffel comes
to mind). And I certainly don't think that inheritance breaks
encapsulation... and I've found dynamic dispatch to not only be very
useful but also to reduce overall code size, simplify architecture, and
in general make software *more* reliable.

Your mileage, of course, may vary. But I just don't see dynamic
dispatch as dangerous in the same way I view global variables as
dangerous...

-->VPutz

Simon Helsen

unread,
Jun 6, 2002, 2:06:04 PM6/6/02
to
On Thu, 6 Jun 2002, Victor B. Putz wrote:

>Hmm... I'm not certain that I agree with the assertion that "every (even
>OO) software designer will tell you: inheritance is dangerous".
>
>I'm a software designer (even OO!), and I don't think inheritance is
>dangerous. I even think that MULTIPLE inheritance is OK if used
>correctly in a language environment that supports it well (Eiffel comes
>to mind). And I certainly don't think that inheritance breaks
>encapsulation... and I've found dynamic dispatch to not only be very
>useful but also to reduce overall code size, simplify architecture, and
>in general make software *more* reliable.

perhaps more reliable, but not more adaptable or maintainable. IMO
inheritence is not the right mechanism to structure problems that are
dynamic and may change very often. Although there is no empirical evidence
yet, I believe parametrised classes (aka mixins or functors in SML - both
different from each other, but targetting the same problem) do not break
encapsulation the way inheritence does and match object-based problem
specifications way better. Additionally, restructuring the design is much
easier. In fact, parametrised classes comprise multiple inheritence in an
unambiguous way. Of course, you may wonder whether dynamic dispatch is
useful with parametrised classes. This could be the case, but the only
paper I know which discusses this, presents a very complex dynamic
dispatch model. I'm not sure if their proposal is tractable for an
everyday programmer. Anyway, this is the general problem with this
(somewhat new) way of viewing classes: it is not ripe and has not been
scaled to large examples/projects. Until that point, I will avoid
inheritence as much as possible whenever I design OO. Btw, most of the
component-based SE lately seems to follow this approach and also provides
ad-hoc solutions for parametrised classes.

>Your mileage, of course, may vary. But I just don't see dynamic
>dispatch as dangerous in the same way I view global variables as
>dangerous...

well yes, of course, but that is no argument in favour of class
inheritence...

Kinds regards,

Simon

Victor B. Putz

unread,
Jun 6, 2002, 4:22:26 PM6/6/02
to
Simon Helsen <hel...@informatik.uni-freiburg.de> writes:

> perhaps more reliable, but not more adaptable or maintainable.

Hmm; I assume this is your viewpoint because often, to really understand
a class, you sorta have to understand the whole tree underneath it?

If so, I concede that it's a pretty valid point. The reason I hesitate
to give it full credence is that all that logic has to go SOMEWHERE, and
at some point it will probably have to be understood whether it's hidden
in an inheritance tree or hidden in a functored class.

*IF* your objection is that as a developer you often deal only with
"leaves" of the tree which expose only a small out-of-context part of
the system's logic, I will say that automated tools can help quite a bit
(again I tout Eiffel, not because I use it but because it has a good
mindset on this; the "short" command will get a flat view of all
features of a class, which can help comprehension considerably).

But I may not be understanding your argument well; could you explain
better why you feel that inheritance impedes adaptability or
maintainability?

> IMO
> inheritence is not the right mechanism to structure problems that are
> dynamic and may change very often. Although there is no empirical evidence
> yet, I believe parametrised classes (aka mixins or functors in SML - both
> different from each other, but targetting the same problem) do not break
> encapsulation the way inheritence does

Okay, you mentioned inheritance breaking encapsulation before as well.
I'm interested in this point of view because of course "encapsulation"
is one of the "virtues" of OO development, and I never really viewed
inheritance as breaking that. Again, could you explain? I'm always
curious about these things.

Also--are you objecting to "interface inheritance" (a la "interfaces" in
Java), "implementation inheritance" (a la C++/Eiffel) or a different
aspect? Want to make sure we're on the same sheet here.

Now I *will* say that a) I don't have a lot of experience with SML
functors (I'm used to "parameterized classes" meaning something more
along the lines of the C++ STL or Eiffel container classes, and "mixins"
bring Ruby to mind--and I don't think either is what you mean), and b) I
think they are fantastically cool, from what little I've seen of them.
So I'm not sure I'm grasping the magnitude of the above paragraph, or
how a parameterized SML class can replace an inheritance tree in a
superior fashion.

> In fact, parametrised classes comprise multiple inheritence in an
> unambiguous way.

Hurm. Brief example, or pointer to same? What little I know of SML
functors has been gleaned from the OCaml tutorials, which have been
quite educational but don't quite make your point.

Interesting conversation; I hope to learn something here.

-->VPutz

Joachim Durchholz

unread,
Jun 6, 2002, 4:38:13 PM6/6/02
to
Simon Helsen wrote:
> parametrised classes [...] do not break

> encapsulation the way inheritence does

Inheritance indeed breaks encapsulation (if you mean "implementation
inheritance", anyway).
This is not a Bad Thing in itself. If the code is large, it's helpful if
you can reuse the implementation. You still have to look at all
functions that you inherit and check whether they are doing what you
really want, iow you need the source code (or a specification that's so
precise that not much is left to the source code - in particular, you
must know which functions are called and what's expected of them, in
case you're overriding one of them).

Interface inheritance is an entirely different thing. I think it's
highly useful - it allows you to vary implementation without changing
the interface, and it gives the users of a library a way to see the
similarities between classes.
Unfortunately, interface inheritance tends to spread concepts across
classes, you need a tool that collects all the information that pertains
to a specific type. (Whether that's a whiz-bang hi-tec class browser or
a humble set of text filters that collect the information into a text
file is irrelevant: you need the tool. Even if it's just a
postprocessing phase of the compiler that produces a "synopsis" of every
class, along with diagnostic information, or info on what optimizations
could be done.)

> In fact, parametrised classes comprise multiple inheritence in an
> unambiguous way.

It's not too difficult to get MI into a conceptual framework that
doesn't leave room for ambiguity, if you know how. (Even "diamond
inheritance" is tractable.)
Unfortunately, no language that I know of gets it right (this includes
C++, Java, Smalltalk, and Eiffel).

> Btw, most of the component-based SE lately seems to follow this
> approach and also provides ad-hoc solutions for parametrised classes.

True.

The latest advice that I heard for Java was
a) keep hierarchies flat
b) make your objects immutable

I'm wondering where I heard this before... ;-)

Anway, that's a "current trend", which might grow into solid advice or
be replaced by the next trend to come.


BTW in Eiffel there's no "keep the hierarchies flat" advice. Hierarchy
depth in Eiffel is a problem because the namespace within the class
tends to become polluted (all those protected helper functions). I have
yet to see an instance of a too-deep interface inheritance hierarchy.
I think this means that interface inheritance (or, equivalently,
subtyping) is still useful.
Dynamic dispatch can be useful in this context. I don't know yet whether
it's worth the trouble or not... the usual consensus among functional
programmers is that it isn't, but then most of them come from a C++
background, meaning that _C++-style_ OO is too dangerous for its
benefits (well, that's no news about C++).

Simon Helsen

unread,
Jun 6, 2002, 7:35:56 PM6/6/02
to
Hi,

somehowe, I think this discussion is not quite appropriate for c.f.l. but
anyway ... :-)

On Thu, 6 Jun 2002, Victor B. Putz wrote:

>Hmm; I assume this is your viewpoint because often, to really understand
>a class, you sorta have to understand the whole tree underneath it?

well, no, it is important to know what is above the particular class (but I
guess you mean that -> a subclass is only understandable whenever you know
about its superclasses, all the way up in the hierarchy)

>If so, I concede that it's a pretty valid point. The reason I hesitate
>to give it full credence is that all that logic has to go SOMEWHERE, and
>at some point it will probably have to be understood whether it's hidden
>in an inheritance tree or hidden in a functored class.

hmmm... I'm not sure if I understand all you're saying here. You can
always put all logic in objects without using inheritence. I've seen and
made OO designs (good ones I beleive) that hardly demand inheritence.

>*IF* your objection is that as a developer you often deal only with
>"leaves" of the tree which expose only a small out-of-context part of
>the system's logic, I will say that automated tools can help quite a bit
>(again I tout Eiffel, not because I use it but because it has a good
>mindset on this; the "short" command will get a flat view of all
>features of a class, which can help comprehension considerably).

Perhaps, but inheritance still breaks encapsulation. What if your
superclass is in a library you're not supposed to look inside? Of course,
in this whole discussion, the real issue is how clear the interfaces for
different classes compose and are known/seen by the developer (you can
port these problems to module systems as well). I'm sure that an
approriate tool helps a lot, but a good language IMO should not rely on
them to express each layer of abstraction (in this case, how components
interact).

>But I may not be understanding your argument well; could you explain
>better why you feel that inheritance impedes adaptability or
>maintainability?

Inheritance is IMO a poor way of structuring code, with a hard way of
understanding the behaviour of an object unless you look all the way up in
the hierarchy. A small change at any level within the path may influence
the behaviour of the objects along the path.

As Joachim says in another post, interfaces and subtyping may help avoid
these problems. In contrast, overriding that does not specialize dangerly
changes the object's semantics.

>Okay, you mentioned inheritance breaking encapsulation before as well.
>I'm interested in this point of view because of course "encapsulation"
>is one of the "virtues" of OO development, and I never really viewed
>inheritance as breaking that. Again, could you explain? I'm always
>curious about these things.

(see the post of Joachim too). The problem is what I roughly described
above. All the objects within one inheritance tree are tied together,
meaning that you cannot treat them as individual objects, i.e. you have to
treat the whole inheritance tree as one "encapsulated object". Now, there
is a need for code-reuse and specialization still. I address that below.

>Also--are you objecting to "interface inheritance" (a la "interfaces" in
>Java), "implementation inheritance" (a la C++/Eiffel) or a different
>aspect? Want to make sure we're on the same sheet here.

No, I'm actually addressing "implementation inheritance".

>Now I *will* say that a) I don't have a lot of experience with SML
>functors (I'm used to "parameterized classes" meaning something more
>along the lines of the C++ STL or Eiffel container classes, and "mixins"
>bring Ruby to mind--and I don't think either is what you mean), and b) I
>think they are fantastically cool, from what little I've seen of them.
>So I'm not sure I'm grasping the magnitude of the above paragraph, or
>how a parameterized SML class can replace an inheritance tree in a
>superior fashion.

Let me first say that I recently heard a talk from which I gathered that
the C++ STL exploit a C++ template feature that roughly maps on
parametrised classes (although they say it is parametrised over the
subclass). In fact, the guy showed how to do generative component
engineering that matches a lot with the ideas of mixins or parametrised
classes. I'm not familiar with Eiffel container classes and neither with
Ruby. I first saw "mixins" in a Scheme context (see link below for a
reference). ML Functors do map fairly well on parametrised classes, except
that they do no provide dynamic dispatch and, of course, they are a module
concept (so, no object vs. class issue). Still, you can use them exactly
in that way and to illustrate, I refer to my post on cfl. a while ago:

http://groups.google.com/groups?q=functor+simon+helsen+group:comp.lang.functional&hl=en&lr=&selm=Pine.LNX.4.33.0202201737500.29688-100000%40waialeale.informatik.uni-freiburg.de&rnum=1

This example shows code-reuse with functors in a way you can still have a
design is factorable. A good type system and type inference with it may be
an important add.

You can treat each functor as class and create objects with a constructor
function within it. Btw, I use this style all the time in my larger SML
programs and although it is not perfect, it is very satisfying from a
design point of view.

>> In fact, parametrised classes comprise multiple inheritence in an
>> unambiguous way.
>
>Hurm. Brief example, or pointer to same? What little I know of SML
>functors has been gleaned from the OCaml tutorials, which have been
>quite educational but don't quite make your point.

I will give you a simple example within the context of SML functors. I
don't know if the example is a typical case for MI, but it comes from an
OO-book. The notation is pseudo to avoid clutter and illustrate the point:

Person
/ \
Student Assitant
\ /
StudentAssitant

pf = functor Person (...) : PERSON =
struct
datatype t = ... (* whatever information you need to keep track of *)
fun new name = ... (* constructor *)
...
end

sf = functor Student (p : PERSON) : STUDENT =
struct ...
datatype t = ... (* probably contains p.t *)
fun new (name, studIdNumber) = ...

... (* here you may re-use code from p as much as you like *)

end
af = functor Assistant (p : PERSON) : ASSISTANT =
struct
datatype t = ... (* idem *)
fun new (name, researchGroup) = ...

... (* idem *)

end
saf = functor StudentAssistant (s : STUDENT, a : ASSISTANT) : STUDENTASSITANT =
struct
datatype t = ... (* probably has both s.t and a.t *)
fun new (name, studIdNumber, researchGroup) = ...

... (* here you can re-used code from both s, a and the two p's
within it if STUDENT and ASSISTENT allow so. In fact, the structures s.p
and a.p may be different if that makes sens, but you, the programmer,
knows what to do with it! *)

end

The initial arguments (superclasses) of Person are unspecified and can be
empty of course. Now, suppose I want to use these functors "somewhere
else", I have code like:

myStudent = saf(p : myPerson)
myStudentAssistant = saf(s : myStudent, a : myAssitant)
val victor = myStudentAssistant.new ("victor", 1111, Graphics)
val simon = myStudent.new ("simon", 9999)

Of course, I can instantiate functors with any structures that match the
signature, for instance, replace it with a more efficient implementation.
Types within signatures play an important role and I realise that this is
partially covered by interface mechanisms in OO-languages. Still, the
above approach is very natural and no need for funky tools.

In the example, I've left out quite some details and you still have to
specify the signatures (PERSON, STUDENT, ASSISTENT and STUDENTASSISTANT),
but you get the idea.

Whether you use modules (functors) with a constructor function or classes
and objects does not make a big difference. Of course, the above mechanism
from ML is quite flexible and strechable too. You may want
functors (or classes) to be higher-order and enrich the "module" or
"object" language, e.g. mutual recursion. If you keep going, you may
actually want a module/class language as powerful as the underlying
(functional) language...

Anwyay, I don't know if this is the final answer to the problems
addressed. I just have the impression that parametrised classes so much
more naturally describe the problem space and are therefore a superiour
design abstraction, compared to classical OO with inheritance. The problem
is that there is not much experience with such techniques, for example,
how well this scales to beyond-toy-examples and what additional mechanisms
are required to support it (for instance, how expressive should interfaces
be? Just types?)

Btw, many functional programmers claim that higher-order functions do this
kind of stuff already. However, I think this is only true for small-scale
programming, not large software designs.

Kind regards,

Simon

Ketil Malde

unread,
Jun 7, 2002, 3:16:07 AM6/7/02
to
Joachim Durchholz <joac...@gmx.de> writes:

> Interface inheritance is an entirely different thing. I think it's
> highly useful - it allows you to vary implementation without changing
> the interface, and it gives the users of a library a way to see the
> similarities between classes.

And as far as I can tell, also available in e.g. Haskell

-- declare an interface Foo
class Foo a where
foo :: ...

-- interface Bar 'inherits' interface Foo
class (Foo a) => Bar a where
bar :: ...

-- type T is an instance of Bar
instance Bar T where
bar = ...

> Unfortunately, interface inheritance tends to spread concepts across
> classes, you need a tool that collects all the information that
> pertains to a specific type.

Could you elaborate what you mean? If interfaces are allowed to
inherit each other, then surely that lets you collect the information?
Am I misreading you?

-kzm
--
If I haven't seen further, it is by standing in the footprints of giants

Andreas Rossberg

unread,
Jun 7, 2002, 6:10:12 AM6/7/02
to
Simon Helsen wrote:
>
> saf = functor StudentAssistant (s : STUDENT, a : ASSISTANT) : STUDENTASSITANT =
> struct
> datatype t = ... (* probably has both s.t and a.t *)
> fun new (name, studIdNumber, researchGroup) = ...
>
> ... (* here you can re-used code from both s, a and the two p's
> within it if STUDENT and ASSISTENT allow so. In fact, the structures s.p
> and a.p may be different if that makes sens, but you, the programmer,
> knows what to do with it! *)

OTOH, if you want to enforce s.p = a.p, then just say

functor StudentAssistant (s : STUDENT, a : ASSISTANT

sharing s.p = a.p)

> Btw, many functional programmers claim that higher-order functions do this
> kind of stuff already. However, I think this is only true for small-scale
> programming, not large software designs.

True, so when you need to bundle function parameters, use modules.
Unfortunately, modules are usually not first-class, which is their main
drawback wrt objects...

- Andreas

Andreas Rossberg

unread,
Jun 7, 2002, 6:09:50 AM6/7/02
to
[Please excuse the accumulative reply.]

Steven Shaw wrote:
>
> "FPL are great because they allow you to do all sorts of interesting
> compiler optimisations". Why then, don't FPL outperform other languages at
> the language shootout?

Because most FPL implementations are result of programming language
research, not compiler construction research.

> Often cited as an argument for FPLs is the quicksort example which is quite
> concise in, say, Haskell. Is it possible for a compiler to compile this
> example into native code that compares with the speed of C?

Since this example operates on lists, while classic imperative sorting
procedures operate on arrays, no fair comparison is possible. Anyway,
quicksort is indeed a bad match for functional data structures, so it's
not too good an example (as has been discussed before).

> I've seen the comment that dynamic dispatch is OOPLs greatest thing. I
> agree. Is is possible to do dynamic dispatch a FPL?

Dynamic dispatch is drastically simplified in FPLs and called
"first-class function".

Some FPLs even have implicit forms of dynamic dispatch, very much like
OOPLs. Think Haskell type classes.

> Just because some popular imperative/oo languages don't have parametric
> polymorphism, type inference or garbage collection doesn't mean that these
> kinds of languages can't have these features.

Type inference doesn't lend itself well to imperative languages, because
declaration and initialisation are usually separated, and because typing
of mutable entities has to be more restrictive.

Parametric polymorphism is much more cumbersome without type inference.

Garbage collection is more costly when used with imperative programming,
because of the usual write barrier.

> What are FPLs best at? Is it compilers?

Yes, and any sort of symbolic computation, tree manipulation, etc.

> Can we have our cake and eat it too? I like the idea of OCaml -
> oo/imperative and functional language combined. Is this the
> way-of-the-future for programming languages?

Personally, I hope not. It makes languages much too heavy and redundant.

> Consider this senario:
> * a problem lends itself to FPL
> * you implement in FPL
> * your program is a success and many new requirements are made
> * the problem doesn't lend itself so much to FPL any more
> * do you: rewrite in OOPL or write new bits in OOPL and interface to
> FPL? Just resign?

Try to rethink the problem, because when it appears to lend itself well
to OOP then my understanding of it must be fundamentally flawed. :-)


Joachim Durchholz <joac...@gmx.de> writes:
>
> Interface inheritance is an entirely different thing. I think it's
> highly useful - it allows you to vary implementation without changing
> the interface, and it gives the users of a library a way to see the
> similarities between classes.

But note that "interface inheritance" is just a crippled
(non-structural) form of subtyping. Proper structural subtyping, as
found in ML modules or in OCaml's objects system, is much more flexible.


"Victor B. Putz" wrote:
>
> Hmm; I assume this is your viewpoint because often, to really understand
> a class, you sorta have to understand the whole tree underneath it?
>

> If so, I concede that it's a pretty valid point. The reason I hesitate
> to give it full credence is that all that logic has to go SOMEWHERE, and
> at some point it will probably have to be understood whether it's hidden
> in an inheritance tree or hidden in a functored class.

But using higher-order functions for example, the "logic" is more
factorised, because each operation is truly parametric in the operations
it depends on.

Joachim Durchholz

unread,
Jun 7, 2002, 7:19:38 AM6/7/02
to
Andreas Rossberg wrote:
> Joachim Durchholz <joac...@gmx.de> writes:
>
>>Interface inheritance is an entirely different thing. I think it's
>>highly useful - it allows you to vary implementation without changing
>>the interface, and it gives the users of a library a way to see the
>>similarities between classes.
>
> But note that "interface inheritance" is just a crippled
> (non-structural) form of subtyping. Proper structural subtyping, as
> found in ML modules or in OCaml's objects system, is much more flexible.

Oh, the advantages of interface inheritance are those of subtyping, so
no disagreement here.

Just being curious: what's "structural subtyping"? (I do have a hunch,
but I might be wrong.)

Andreas Rossberg

unread,
Jun 7, 2002, 8:15:15 AM6/7/02
to
Joachim Durchholz wrote:
>
> Just being curious: what's "structural subtyping"? (I do have a hunch,
> but I might be wrong.)

It denotes a subtyping relation that is defined by standard inductive
rules over the structure of types. Unlike e.g. interfaces in Java, where
the programmer has to declare subtyping between named types and there is
only a trivial transitivity rule built-in (plus a small bunch of adhoc
structural rules like for arrays).

Structural subtyping is more flexible because it applies `after the
fact'. In Java, C++, Eiffel and similar languages you have to know and
declare any interface subtyping you might need beforehand.

Brian Rogoff

unread,
Jun 7, 2002, 12:28:31 PM6/7/02
to
On Fri, 7 Jun 2002, Andreas Rossberg wrote:
> Unfortunately, modules are usually not first-class, which is their main
> drawback wrt objects...

Yes, that's true. However, it seems that you're viewing classes as a
programming construct which competes with modules. I usually view them
as a construct which competes with records. In OCaml at least, you can use
them to get around some restrictions on records that tend to be annoying,
like the inability of record types to share field label names.

Once you start making modules first class, I begin to wonder why we need
to have records and modules, but alas, I'm not a language theorist so I
just retreat back to the mundane world of VLSI design...

-- Brian


Jeffrey Straszheim

unread,
Jun 7, 2002, 4:32:42 PM6/7/02
to
On Fri, 07 Jun 2002 13:19:38 +0200, Joachim Durchholz
<joac...@gmx.de> wrote:

> Just being curious: what's "structural subtyping"? (I do have a
> hunch, but I might be wrong.)

In structural subtyping, the type system will infer that function X
requires an object that has methods A, B, and C; of types Q, R, and
S. Any object that has at least that interface will be properly a
subtype. You never need to explicitly declare interfaces (object
types) except to save typing.

The same principle can hold for record types.

This is opposed to *nominal* subtyping, where types have specific
names, and subtyping requires that a class be notated as subtyping
that specific name, plus that it implement its structure. This is how
Java, C++, and Eiffel work.

-- Jeffrey Straszheim | A sufficiently advanced
-- Programmer, Math Geek | regular expression is
-- http://www.shadow.net/~stimuli | indistinguishable from
-- stimuli AT shadow DOT net | magic

Aaron Denney

unread,
Jun 7, 2002, 6:11:43 PM6/7/02
to
Andreas Rossberg <ross...@ps.uni-sb.de> wrote:

> Steven Shaw wrote:
> > Just because some popular imperative/oo languages don't have parametric
> > polymorphism, type inference or garbage collection doesn't mean that these
> > kinds of languages can't have these features.
>
> Type inference doesn't lend itself well to imperative languages, because
> declaration and initialisation are usually separated, and because typing
> of mutable entities has to be more restrictive.
>
> Parametric polymorphism is much more cumbersome without type inference.
>
> Garbage collection is more costly when used with imperative programming,
> because of the usual write barrier.

Sather is imperative, and has all three of these features. There isn't
as much conflict as you might think.

--
Aaron Denney
-><-

Albert Lai

unread,
Jun 8, 2002, 3:55:17 AM6/8/02
to
"Steven Shaw" <steve...@uq.net.au> writes:

> "FPL are great because they allow you to do all sorts of interesting
> compiler optimisations". Why then, don't FPL outperform other languages at
> the language shootout?

I think functional languages have outperformed other languages
already. How many years, researchers, PhD theses, and hackers have
been thrown at optimizing C programs? And how many have worked on
optimizing OCaml programs? I would guess the ratio is about 500:1,
but it is ridiculously disproportionate anyway. Now, given this
overwhelming advantage, the C program is maybe faster by just 2 to 5
times. I think this says alot about the futility of C optimization
and the opportunity in functional language optimization, contrary to
common superficial thinking.

Markus Mottl h9303956@wu-wien.ac.at

unread,
Jun 8, 2002, 5:21:41 AM6/8/02
to
Albert Lai <tre...@vex.net> wrote:
> I think functional languages have outperformed other languages already.
> How many years, researchers, PhD theses, and hackers have been thrown
> at optimizing C programs? And how many have worked on optimizing
> OCaml programs? I would guess the ratio is about 500:1, but it is
> ridiculously disproportionate anyway. Now, given this overwhelming
> advantage, the C program is maybe faster by just 2 to 5 times.

This is probably a strong overestimation. I'd say that the average
at which thorougly optimized C-programs run faster is lower than 50%.
There were extreme cases where OCaml had a small edge against C (gcc)
and other extreme ones (I think I once saw some fancy sorting routine)
where C dominated by a factor of 2-4.

Many people believe that the OCaml-compiler performs tricky optimizations,
but in reality it just generates excellent machine code and doesn't do
anything about high-level transformations (e.g. CSE). This is both a
consequence of having the requirement of fast compilation and also of
not wanting to waste human resources on not so important features. The
intention is to give programmers a more expressive and safe language,
which makes it easier for them to implement sophisticated algorithms. As
we surely all agree, it's most often the choice of algorithms that
decides whether a program runs fast or not.

> I think this says alot about the futility of C optimization and the
> opportunity in functional language optimization, contrary to common
> superficial thinking.

Otherwise, you are definitely right: if this were of greater
importance, it would surely be possible to throw more human resources
into implementation of FPL-optimizations. At least what concerns me,
I have to say that the performance of OCaml and also of some other FPLs
is sufficient for my efficiency needs, the latter probably being above
average (machine learning).

Joachim Durchholz

unread,
Jun 9, 2002, 8:33:23 AM6/9/02
to
Sorry for the late reply, but it took me some time to get around to
understanding the details.

OK, right.
This is actually a rather typical OO pattern, using delegation to
implement multiple inheritance. I see no problems with this in the
absence of mutation (including the absence of a "copy-and-modify
constructor").
If you have a function that needs access to the Student data, you pass
it s, if that function needs an Assistant, you give it a, and if it just
needs a Person, you give it s.p or a.p (and if the two p's are
different, which is not an uncommon occurrence in real life, you even
get to decide which of them is the right one for the particular case - a
choice that no OO language that I know of gives you).

The downside is that you don't have polymorphism. Which isn't going to
make much of a difference with immutable data, but Person data tends to
change (people marry, relocate, or whatever).
For example, assume that Person has an Address component. Normally, the
Student and Assistant addresses are the same, but at some point during
evolution of the software, it turned out that some people want their
mail sent to different addresses depending on whether the mail goes to
them in their role as a student or assistant. So you have some instances
where the Person data is identical ans some where it isn't.
Since the name must be the same, you need a special update procedure
that, whenever one Person record has its name modified, the record in
the other will be modified as well so that the two are consistent. (I
know that this design is bad, but in practice you don't always have the
time to remove all wrinkles. Besides, the consistency conditions can be
more subtle than just a redundant copy of data; this is really a toy
example that's already too large for Usenet discussions *g*.)

Now, how should this consistency be guaranteed?
The easiest way would be to add an "alias" component to Person, and
change the Person functor to update the alias whenever the name is modified.
The problem is that this means that the design problem now affects two
functors, Person and StudentAssistant. You can quickly bring down the
structure of any module by adding a few dozen modifications of this type.

With polymorphism, I can do something different: I can add a change_name
procedure in StudentAssistant, and polymorphism will make sure that the
"right" routine is called even if just the Person object was passed to
some other function. Ideally, that function (e.g. something that batch
processes a series of name changes into a List of Persons) never sees
the side effect (of course, it's exactly the side effects that make
polymorphism both useful and dangerous).

Note that this is not implementation inheritance, but indeed subtyping.
Actually I have a hunch that the subtype property will break down as
soon as you include side effects into the type regime, but then not
everything need be done in the type system.

Note that diamond inheritance doesn't work really good in any OO
language that I know of. If the update is asymmetric (e.g. you want the
name changed in both if the change is from the Student side, but not if
it is from the Assistant side - I know this is getting silly, but this
is a toy example and you tend to have this sort of requirement in
practice), then you can't simply pass around a Person interface: the
StudentAssistant object will have two Person interfaces, one for Student
and one for Assisant. In other words, if you pass sa.s.p to a function
expecting a Person, you'll get a different behaviour from that what
you'd get if you passed in sa.a.p.

And all of this is irrelevant for immutable objects - I slow begin to
understand why inheritance is considered secondary for functional
programmers...
But I'm not sure that it can be dispensed of in its entirety. Stuff like
StudentAssistant is commonplace, and in practice, a lot of mutation is
going on. This all can (of course) be mapped on some HOF functions that
mimick dispatch tables, at exactly the granularity that's desired. But
I'm not sure whether homemade dispatch tables are better than built-in
ones - the issues are tricky, as I hopefully have demonstrated, and I
suspect that language support would be helpful. (Well, maybe a good
library could handle this type of problem just as well. I just think
that a framework for this stuff would help eliminate thinkos.)

> Of course, I can instantiate functors with any structures that match the
> signature, for instance, replace it with a more efficient implementation.
> Types within signatures play an important role and I realise that this is
> partially covered by interface mechanisms in OO-languages. Still, the
> above approach is very natural and no need for funky tools.

Uh, well, yes.
OTOH you cannot do things like "a square is a rectangle that has equal
sides". And half a dozen other things that help refactoring your code.

Steven Shaw

unread,
Jun 10, 2002, 1:53:37 AM6/10/02
to
"Andreas Rossberg" <ross...@ps.uni-sb.de> wrote in message
news:3D00A3D3...@ps.uni-sb.de...

> Structural subtyping is more flexible because it applies `after the
> fact'. In Java, C++, Eiffel and similar languages you have to know and
> declare any interface subtyping you might need beforehand.

I think there was a feature like this added to GCC at one time (it's been
removed since). It was called "signatures".


Steven Shaw

unread,
Jun 10, 2002, 2:20:56 AM6/10/02
to

"Simon Helsen" <hel...@informatik.uni-freiburg.de> wrote in message
news:Pine.LNX.4.33.020606...@waialeale.informatik.uni-freib
urg.de...

> >I've seen the comment that dynamic dispatch is OOPLs greatest thing. I
> >agree. Is is possible to do dynamic dispatch a FPL?
>
> dynamic dispatch? Great? Dynamic dispatch is bound to inheritence. As
> every (even OO) software designer will tell you: inheritence is dangerous
> and should be avoided as much as possible (because it breaks encapsulation
> -> read the COM-model of MS as an example). Dynamic dispatch? Why?

I don't think you understand what I think is good about dynamic dispatch.
It's
not bound to inheritence (as you can have dynamic dispatch in C - all you
need is function pointers and indirect function call). It can be bound
to inheritence but in languages like Java and C# it can be interface
inheritence
(so you can avoid this "dangerous" type of inheritence is you want).

What's good about dynamic dispatch? Here's a an example of what I find
useful:
If I want to separate my program from it's configuration data, I might right
a Java-style interface, IConfiguration, that allows loading configuration
and access to
configuration data. Then I might write different classes that support that
interface, say
FileConfiguration, DbConfiguration, MemoryConfiguration (or something). Then
at runtime I could dynamically load one of these classes, make an object and
use
that anywher the IConfiguration interface is needed.

I know that Lisp can do dynamic dispatch - although it doesn't support the
naming/typing
of these interfaces. I'm not sure that it's supported with ML or Haskell. I
imagine that OCaml
supports it via it's object system.

Steve.


Steven Shaw

unread,
Jun 10, 2002, 2:24:41 AM6/10/02
to
"Simon Helsen" <hel...@informatik.uni-freiburg.de> wrote in message
news:Pine.LNX.4.33.020606...@waialeale.informatik.uni-freib
urg.de...
> Modula-3 is age-old and died out. Although it is indeed a cool language,
> it IMO surpassed by modern FPLs. Of course, you can add great features to
> imperative languages, but it hardly happens! (Smalltalk is an exception in
> so far that it has HOFs, but it is dynamically typed)

I'm glad you like Modula-3. I'm liking it more, the more I read about it.
It's
not really that old and hasn't completely died out. Some people are building
an extensible operating system with it (at Washington University I think).

Steve.


Steven Shaw

unread,
Jun 10, 2002, 2:55:51 AM6/10/02
to

"Andreas Rossberg" <ross...@ps.uni-sb.de> wrote in message
news:3D00866E...@ps.uni-sb.de...

>
> > Often cited as an argument for FPLs is the quicksort example which is
quite
> > concise in, say, Haskell. Is it possible for a compiler to compile this
> > example into native code that compares with the speed of C?
>
> Since this example operates on lists, while classic imperative sorting
> procedures operate on arrays, no fair comparison is possible. Anyway,
> quicksort is indeed a bad match for functional data structures, so it's
> not too good an example (as has been discussed before).

I have heard that there are ways to "declare an imperative". Is it not
possible to do
this somehow with quicksort?

> > I've seen the comment that dynamic dispatch is OOPLs greatest thing. I
> > agree. Is is possible to do dynamic dispatch a FPL?
>
> Dynamic dispatch is drastically simplified in FPLs and called
> "first-class function".

I understand that this works with Lisp family languages but what about
ML-family?

> Some FPLs even have implicit forms of dynamic dispatch, very much like
> OOPLs. Think Haskell type classes.

I don't know alot about Haskell type classes, however as I understand it,
it, Haskell is statically typed. This means no dynamic dispatch happens.
I think what you as describing is a form of polymorphism but
it is parametric polymorphism rather than inclusion polymorphism.

> > Just because some popular imperative/oo languages don't have parametric
> > polymorphism, type inference or garbage collection doesn't mean that
these
> > kinds of languages can't have these features.
>
> Type inference doesn't lend itself well to imperative languages, because
> declaration and initialisation are usually separated, and because typing
> of mutable entities has to be more restrictive.

They don't have to be. They aren't, for instance, in Modula-3 or C++ (though
they can be).
Modula-3's type inference only infers types when variables and parameters
are initialised (or
given defaults).

Type inference does seem to be one place where functional languages have a
clear
advantage over imperative languages. With functional languages, you can
leave you leave your
types out and create a generic function. With a language like Modula-3 you
have to explicitly
use a generic module if you want parametric polymorphism.

> Parametric polymorphism is much more cumbersome without type inference.

Yes.

> Garbage collection is more costly when used with imperative programming,
> because of the usual write barrier.

Do you know anywhere I could read more about this?
I understand that this write-barrier is used with generational gc with
imperative languages. I guess it's not need with purely functional languages
because there are no mutations? Couldn't this be offset by the fact that
with functional languages you have likely to be creating new values at
a much higher rate than with imperative languages?

> > What are FPLs best at? Is it compilers?
>
> Yes, and any sort of symbolic computation, tree manipulation, etc.

Cheers,
Steve.


Remi VANICAT

unread,
Jun 10, 2002, 3:47:26 AM6/10/02
to
"Steven Shaw" <steve...@uq.net.au> writes:

You can do it with record of higher order function in many (if not all)
functional language.

something like :

type configuration =
{
read : unit -> unit
write : unit -> unit
get_value : value_ident -> conf_value
set_value : value_ident -> conf_value -> unit
}

(this is ocaml without object, value_ident and conf_value are supposed
to be two type one that contain identifier for the configuration
thing, the other the value that can be in).

similar thing can be done with Haskell class, and so on.

--
Rémi Vanicat
van...@labri.u-bordeaux.fr
http://dept-info.labri.u-bordeaux.fr/~vanicat

Andreas Rossberg

unread,
Jun 10, 2002, 6:31:21 AM6/10/02
to
Brian Rogoff wrote:

>
> However, it seems that you're viewing classes as a
> programming construct which competes with modules. I usually view them
> as a construct which competes with records. In OCaml at least, you can use
> them to get around some restrictions on records that tend to be annoying,

> like the inability of record types to share field label names.


To me objects are sort of middle ground between records and modules.
They share with records that they are first class citizens, can be
recursively typed, cannot contain type members, and so on. On the other
hand they have in common with modules that they rely on subtyping, are
usually more heavyweight (in particular because they must be typed more
explicitly), and tend to provide nested polymorphism. Of course, some of
these tradeoffs may vanish with future research.

BTW, this fuzziness in their tradeoffs is one of the reasons why I
rarely find good use for objects: usually it seems more consequent to
either use records or modules. But that may be a problem of my personal
mindset...

> Once you start making modules first class, I begin to wonder why we need
> to have records and modules,


For very pragmatic reasons. Records should be lightweight and support
type inference. Modules on the other hand provide a very powerful typing
discipline which requires explicit annotations and must be carefully
designed to remain decidable. In particular, they rely on a form of
dependent typing.

If you consider a language with general dependent types (e.g. Cayenne)
then there is indeed no point in separating the two (consequently,
Cayenne doesn't). But then you have to live with undecidable type
checking and very limited type inference anyway.

Joachim Durchholz

unread,
Jun 10, 2002, 7:30:15 AM6/10/02
to
Remi VANICAT wrote:

> "Steven Shaw" <steve...@uq.net.au> writes:
>>If I want to separate my program from it's
>>configuration data, I might right a Java-style interface,
>>IConfiguration, that allows loading configuration and access to
>>configuration data. Then I might write different classes that
>>support that interface, say FileConfiguration, DbConfiguration,
>>MemoryConfiguration (or something). Then at runtime I could
>>dynamically load one of these classes, make an object and use that
>>anywher the IConfiguration interface is needed.
>
> You can do it with record of higher order function in many (if not all)
> functional language.

Right - but a well-designed OO language can give you guarantees about
the subclasses that a non-OO language (functional or not) cannot give
you. (Though neither C++ nor Java give you much in the way of explicit
guarantees. I'm more thinking along the lines of Design by Contract.)

Markus Mottl h9303956@wu-wien.ac.at

unread,
Jun 10, 2002, 7:34:41 AM6/10/02
to
Andreas Rossberg <ross...@ps.uni-sb.de> wrote:
> BTW, this fuzziness in their tradeoffs is one of the reasons why I
> rarely find good use for objects: usually it seems more consequent
> to either use records or modules. But that may be a problem of my
> personal mindset...

This is also my experience: algebraic datatypes + records, the latter
actually just being tuples that allow access by name rather than position,
together with modules seem to be the better choice in the vast majority
of design problems.

Regards,
Markus

Andreas Rossberg

unread,
Jun 10, 2002, 7:58:29 AM6/10/02
to
Steven Shaw wrote:
>

>> Since this example operates on lists, while classic imperative sorting
>> procedures operate on arrays, no fair comparison is possible. Anyway,
>> quicksort is indeed a bad match for functional data structures, so it's
>> not too good an example (as has been discussed before).
>
> I have heard that there are ways to "declare an imperative". Is it not

> possible to do this somehow with quicksort?


Sure it's possible but it would not look as neat as that toy example.
When you implement imperative algorithms they will look imperative, even
in a functional language.

>>> I've seen the comment that dynamic dispatch is OOPLs greatest thing. I
>>> agree. Is is possible to do dynamic dispatch a FPL?
>>
>> Dynamic dispatch is drastically simplified in FPLs and called
>> "first-class function".
>
> I understand that this works with Lisp family languages but what about

> ML-family?


You probably missed my bit of sarcasm there. First-class functions are
THE fundamental ingredient of any language calling itself functional. I
didn't mean complicated stuff like CLOS multi-methods.

>> Some FPLs even have implicit forms of dynamic dispatch, very much like
>> OOPLs. Think Haskell type classes.
>
> I don't know alot about Haskell type classes, however as I understand it,
> it, Haskell is statically typed. This means no dynamic dispatch happens.


Huh? Why do you think dynamic dispatch is in conflict with static
typing? After all, most of today's OOPLs are statically typed as well,
aren't they?

> I think what you as describing is a form of polymorphism but

> it is parametric polymorphism rather than inclusion polymorphism.


But dynamic dispatch is not necessarily connected to "inclusion
polymorphism". That's typical for OO only.

>> Garbage collection is more costly when used with imperative programming,
>> because of the usual write barrier.
>
> Do you know anywhere I could read more about this?


A good starter for garbage collection is

Jones, Richard; Lins, Rafael
"Garbage Collection"
John Wiley and Sons, 1996

Off hand I don't know any good literature that discusses GC in the
particular context of FPLs. But I'm no GC expert.

> I understand that this write-barrier is used with generational gc with
> imperative languages. I guess it's not need with purely functional languages
> because there are no mutations?


Well, almost all FPLs support imperative programming in one way or the
other. Some do a fair amount of mutation under the hood (e.g. to
implement layiness). So their GCs still need write barriers. But the
point is that mutations happen much less frequently than in imperative
programming.

> Couldn't this be offset by the fact that
> with functional languages you have likely to be creating new values at
> a much higher rate than with imperative languages?


That's why FPLs usually use generational GC where allocation in the
youngest generation is tuned to be very cheap. Since most objects are
short-lived only comparably few need to be touched during GC.

Steven Shaw

unread,
Jun 10, 2002, 8:15:49 AM6/10/02
to
"Remi VANICAT" <vanicat...@labri.fr> wrote in message
news:87it4ry...@wanadoo.fr...

> > I know that Lisp can do dynamic dispatch - although it doesn't
> > support the naming/typing of these interfaces. I'm not sure that
> > it's supported with ML or Haskell. I imagine that OCaml supports it
> > via it's object system.
>
> You can do it with record of higher order function in many (if not all)
> functional language.
>
> something like :
>
> type configuration =
> {
> read : unit -> unit
> write : unit -> unit
> get_value : value_ident -> conf_value
> set_value : value_ident -> conf_value -> unit
> }
>
> (this is ocaml without object, value_ident and conf_value are supposed
> to be two type one that contain identifier for the configuration
> thing, the other the value that can be in).

Thanks, that helps alot. This is the same way you'd do it in C.
How do you construct a value of this type? I assume that you must set
all the function values at construction time (that's good, though).

Is there a way to dynamically load code that implements this "interface"?

Steve.


Simon Helsen

unread,
Jun 10, 2002, 7:56:27 AM6/10/02
to
On Mon, 10 Jun 2002, Steven Shaw wrote:

>What's good about dynamic dispatch? Here's a an example of what I find
>useful:
>If I want to separate my program from it's configuration data, I might right
>a Java-style interface, IConfiguration, that allows loading configuration
>and access to
>configuration data. Then I might write different classes that support that
>interface, say
>FileConfiguration, DbConfiguration, MemoryConfiguration (or something). Then
>at runtime I could dynamically load one of these classes, make an object and
>use
>that anywher the IConfiguration interface is needed.

I don't quite understand. Why do you need *dynamic* dispatch? Any even
relative simple module language can express this. The actual functions are
bound at compile or link-time. I fail to see the advantage of dynamically
resolving what method is called. It is dangerous, obscures the code and
may lead to abuse of the dynamically bound function (i.e.
spagethi-code). That is my general problem with OO: you end up developing
large monolithic structures, even at design-level...

Regards,

Simon

Simon Helsen

unread,
Jun 10, 2002, 8:15:25 AM6/10/02
to
On Sun, 9 Jun 2002, Joachim Durchholz wrote:

>>
>> Person
>> / \
>> Student Assitant
>> \ /
>> StudentAssitant
>>
>> pf = functor Person (...) : PERSON =
>> struct
>> datatype t = ... (* whatever information you need to keep track of *)
>> fun new name = ... (* constructor *)
>> ...
>> end
>>
>> sf = functor Student (p : PERSON) : STUDENT =
>> struct ...

>OK, right.
>This is actually a rather typical OO pattern, using delegation to
>implement multiple inheritance. I see no problems with this in the
>absence of mutation (including the absence of a "copy-and-modify
>constructor").
>If you have a function that needs access to the Student data, you pass
>it s, if that function needs an Assistant, you give it a, and if it just
>needs a Person, you give it s.p or a.p (and if the two p's are
>different, which is not an uncommon occurrence in real life, you even
>get to decide which of them is the right one for the particular case - a
>choice that no OO language that I know of gives you).

glad you agree.

>The downside is that you don't have polymorphism. Which isn't going to
>make much of a difference with immutable data, but Person data tends to
>change (people marry, relocate, or whatever).

?

>For example, assume that Person has an Address component. Normally, the
>Student and Assistant addresses are the same, but at some point during
>evolution of the software, it turned out that some people want their
>mail sent to different addresses depending on whether the mail goes to
>them in their role as a student or assistant. So you have some instances
>where the Person data is identical ans some where it isn't.
>Since the name must be the same, you need a special update procedure
>that, whenever one Person record has its name modified, the record in
>the other will be modified as well so that the two are consistent. (I
>know that this design is bad, but in practice you don't always have the
>time to remove all wrinkles. Besides, the consistency conditions can be
>more subtle than just a redundant copy of data; this is really a toy
>example that's already too large for Usenet discussions *g*.)

I don't understand why you cannot express that naturally in the model of
my previous post. The changing functionality here requires changing the
StudentAssistent functor. Within, you add something like

addHomeAddress addr = s.p.addAdress addr
addUniAddress addr = a.p.addAddress addr

with the interface STUDENTASSISTANT extended to
addHomeAddress : addrType -> unit
addUniAddress : addrType -> unit

and of course you remove the addAddress function from STUDENTASSITANT,
i.e. you do not export it anymore. This is assuming you use state and
side-effects. I think in this case I would rather construct new "objects"
with the particular property.

Either way, clear design, no inconsistency problems etc.

Mind that you do *not* inherit your "superclass" (aka functor-argument).
You only take whatever you need and you only export whatever you need.

>With polymorphism, I can do something different: I can add a change_name
>procedure in StudentAssistant, and polymorphism will make sure that the
>"right" routine is called even if just the Person object was passed to
>some other function. Ideally, that function (e.g. something that batch
>processes a series of name changes into a List of Persons) never sees
>the side effect (of course, it's exactly the side effects that make
>polymorphism both useful and dangerous).

but you see, that's the whole point. I don't want that the "right" routine
is called indirectly. It is dangerous and obscure. Still, the
design-possibilities of the above outlined functor-method are pretty
powerful. Some programmer X looking at functor A with interface AI and
argument B with interface BI, knows what he gots. No magic method
allocation going on here!

>Note that diamond inheritance doesn't work really good in any OO
>language that I know of. If the update is asymmetric (e.g. you want the
>name changed in both if the change is from the Student side, but not if
>it is from the Assistant side - I know this is getting silly, but this
>is a toy example and you tend to have this sort of requirement in
>practice), then you can't simply pass around a Person interface: the
>StudentAssistant object will have two Person interfaces, one for Student
>and one for Assisant. In other words, if you pass sa.s.p to a function
>expecting a Person, you'll get a different behaviour from that what
>you'd get if you passed in sa.a.p.

the issue is that in the functor-method you don't think in terms of the
dymond property. You have a functor which is parametrised by an interface
really, it implements an interface and that's it! The rest is glued
together in the way other components use them.

>Uh, well, yes.
>OTOH you cannot do things like "a square is a rectangle that has equal
>sides". And half a dozen other things that help refactoring your code.

what do you mean, you can't do that?

rt = functor Rectangle (...) : RECTANGLE =
struct
type t = ...
new (base, hight) = ...
surface (r: t) = getBase r * getHight r
end
st = functor Square (r...) : SQUARE =
struct
type t = r.t
new (side : int) = r.new (side, side)
surface (s : t) = sqr(getSide s)
end

where of course the constructor function new has a different type in
RECTANGLE or SQUARE... (you can have the same interface, but then you need
to throw an exception in SQUARE if the base <> hight. Either way,
the invariant is guaranteed by the functor implementing SQUARE!

Regards,

Simon

Remi VANICAT

unread,
Jun 10, 2002, 8:42:24 AM6/10/02
to
"Steven Shaw" <steve...@uq.net.au> writes:

> "Remi VANICAT" <vanicat...@labri.fr> wrote in message
> news:87it4ry...@wanadoo.fr...
> > > I know that Lisp can do dynamic dispatch - although it doesn't
> > > support the naming/typing of these interfaces. I'm not sure that
> > > it's supported with ML or Haskell. I imagine that OCaml supports it
> > > via it's object system.
> >
> > You can do it with record of higher order function in many (if not all)
> > functional language.
> >
> > something like :
> >
> > type configuration =
> > {
> > read : unit -> unit
> > write : unit -> unit
> > get_value : value_ident -> conf_value
> > set_value : value_ident -> conf_value -> unit
> > }
> >
> > (this is ocaml without object, value_ident and conf_value are supposed
> > to be two type one that contain identifier for the configuration
> > thing, the other the value that can be in).
>
> Thanks, that helps alot. This is the same way you'd do it in C.
> How do you construct a value of this type?

let say you have two function, read_config that when given a file name
return an hashtable from key to value, and write_config that take such
a hastable and a file name and write it :

let make_conf file_name =
let table = ref None in
let get_table () =
match !table with
| None -> failwith "no config file load"
| Some x -> x in
let read () = table <- (Some (read_config file_name))
and write () =
write_config file_name (get_table ())
and get_value i = Hastbl.find (get_table ()) i
and set_value i v = Hastbl.replace (get_table ()) i v in
{
read = read; write = write; get_value = get_value;
set_value = set_value
}

for example (the idea is that the 4 closure will contain any
thing that is needed for real function to do the work, but aren't in
the argument of the function in the record).

This code is not cleanly functional though (it's even heavily
imperative), but the idea is there, and better things can be done.


> I assume that you must set all the function values at construction
> time (that's good, though).

Yes.

>
> Is there a way to dynamically load code that implements this
> "interface"?

In ocaml, the Dynlink module will do it.

Christopher Browne

unread,
Jun 10, 2002, 9:40:23 AM6/10/02
to
Quoth "Steven Shaw" <steve...@uq.net.au>:

That was SPIN. <http://www-spin.cs.washington.edu/>.

It is largely a "dead project;" the major research took place back in
1996-1997, and newer work doesn't seem to be terribly strongly tied to
SPIN.

It's very unfortunate that M3 is considered to have "died out;" it
seems quite a lot nicer than most of its close competitors...
--
(reverse (concatenate 'string "moc.enworbbc@" "sirhc"))
http://www.cbbrowne.com/info/finances.html
Rules of the Evil Overlord #154. "I will instruct my Legions of Terror
in proper search techniques. In particular, if they are searching for
escapees and someone shouts, "Quick! They went that way!", they must
first ascertain the identity of this helpful informant before dashing
off in hot pursuit." <http://www.eviloverlord.com/>

Andreas Rossberg

unread,
Jun 10, 2002, 10:31:13 AM6/10/02
to
Simon Helsen wrote:
>

>> OTOH you cannot do things like "a square is a rectangle that has equal
>> sides". And half a dozen other things that help refactoring your code.
>>
>
> what do you mean, you can't do that?
>
> rt = functor Rectangle (...) : RECTANGLE =
> struct
> type t = ...
> new (base, hight) = ...
> surface (r: t) = getBase r * getHight r
> end
> st = functor Square (r...) : SQUARE =
> struct
> type t = r.t
> new (side : int) = r.new (side, side)
> surface (s : t) = sqr(getSide s)
> end
>
> where of course the constructor function new has a different type in
> RECTANGLE or SQUARE... (you can have the same interface, but then you need
> to throw an exception in SQUARE if the base <> hight. Either way,
> the invariant is guaranteed by the functor implementing SQUARE!


Well, not this way. In SML you have to use opaque signature ascription
to make any instance of Square's type t different from Rectangle's t:

> rt = functor Rectangle (...) :> RECTANGLE =
> struct
> type t = ...
> new (base, hight) = ...
> surface (r: t) = getBase r * getHight r
> end

> st = functor Square (r...) :> SQUARE =
> struct
> type t = r.t
> new (side : int) = r.new (side, side)
> surface (s : t) = sqr(getSide s)
> end


Otherwise, any client can fake squares by rectangles, breaking your
invariants.

Simon Helsen

unread,
Jun 10, 2002, 10:43:14 AM6/10/02
to
On Mon, 10 Jun 2002, Andreas Rossberg wrote:

>Well, not this way. In SML you have to use opaque signature ascription
>to make any instance of Square's type t different from Rectangle's t:
>
>> rt = functor Rectangle (...) :> RECTANGLE =
>> struct
>> type t = ...
>> new (base, hight) = ...
>> surface (r: t) = getBase r * getHight r
>> end
>
>> st = functor Square (r...) :> SQUARE =
>> struct
>> type t = r.t
>> new (side : int) = r.new (side, side)
>> surface (s : t) = sqr(getSide s)
>> end
>
>
>Otherwise, any client can fake squares by rectangles, breaking your
>invariants.

yes, I know. I didn't want to go into this to avoid confusion. Also, this
is the particular way these things are solved in ML-style functors. I was
thinking on a broader level where a system of parametrised classes can
express something similar.

Regards,

Simon

Victor B. Putz

unread,
Jun 10, 2002, 2:33:59 PM6/10/02
to
Simon Helsen <hel...@informatik.uni-freiburg.de> writes:

> I don't quite understand. Why do you need *dynamic* dispatch?

Simon, again I must admit my ignorance of the functor mindset. A very
brief big-picture explanation would help.

For example, I recently wrote a very simple little "virtual filesystem"
module in (gasp) C++; you had several different "plug-in" classes, each
representing files stored in a different way (for example, a system_fs
class which represented a directory on disk, and a zip_fs class which
represented a zip file). The main "root" fs object just maintained a
list of filesystems and looked them up by name, so the interface was
very simple: ask for a file system by name, and you get a usable file
system object with a consistent interface.

The object you recieved (a basic_fs object) would dynamically dispatch
calls; the consumer doesn't know if it has a system_fs or a zip_fs
object, and more to the point it doesn't care.

In this case, dynamic dispatch made a pretty simple design; keep a list
of basic_fs objects (which could by system_fs or zip_fs objects, but the
owner doesn't care) and hand them out to clients, who also don't care
what the actual class is.

Admittedly, I'm a fairly straightforward person, and I haven't worked
with functor-style modules, but how would they make this simpler? Why
isn't the simple, basic dynamic dispatch approach the best?

-->VPutz

Simon Helsen

unread,
Jun 10, 2002, 3:38:47 PM6/10/02
to
Hi Victor,

On Mon, 10 Jun 2002, Victor B. Putz wrote:

>For example, I recently wrote a very simple little "virtual filesystem"
>module in (gasp) C++; you had several different "plug-in" classes, each
>representing files stored in a different way (for example, a system_fs
>class which represented a directory on disk, and a zip_fs class which
>represented a zip file). The main "root" fs object just maintained a
>list of filesystems and looked them up by name, so the interface was
>very simple: ask for a file system by name, and you get a usable file
>system object with a consistent interface.
>
>The object you recieved (a basic_fs object) would dynamically dispatch
>calls; the consumer doesn't know if it has a system_fs or a zip_fs
>object, and more to the point it doesn't care.
>
>In this case, dynamic dispatch made a pretty simple design; keep a list
>of basic_fs objects (which could by system_fs or zip_fs objects, but the
>owner doesn't care) and hand them out to clients, who also don't care
>what the actual class is.

okay, I see why you think dynamic dispatch is useful. I still beleive it
is debatable whether the "dynamicness" of the dispatch is a good thing. In
the ML-functor approach, this problem is easily solved by writing the
dispatch code yourself. You may say that this is "extra" work. Perhaps,
but it is strong documentation. Concrete for your problem:

bfs = functor BasicFS (...) : BASICFILESYSTEM =
struct ... end
sfs = functor SystemFS (bfs : FILESYSTEM) : SYSTEMFILESYSTEM
(* probably includes BASICFILESYSTEM *) =
struct ... end
zfs = functor ZipFS (bfs : FILESYSTEM) : ZIPFILESYSTEM
(* idem *) =
struct ... end

now, you do not give your client an object created by the BasicFS
structure (functor), but you create an actual interface functor

fs = functor FileSystem (sfs : SYSTEMFILESYSTEM
zfs : ZIPFILESYSTEM) : FILESYSTEM =
struct
datatype fsType =
ZipFS of zfs.t
| SystemFS of sfs.t
type t = fsType list

newZipFs pars = ... (* calls zfs.new ... *)
newSystemFs pars = ... (* calls sfs.new ... *)

fileName (fs: t, name : string)=
case fs of
ZipFS zipfs = zfs.fileName (zipfs, name)
| SystemFS systemfs = sfs.fileName (systemfs, name)

...
end

You may wonder why this good? Well, in rigid OO-thinking, you shouldn't
consider BasicFS as a FILESYSTEM. It is probably an abstract class and
even if it isn't, when you hand the client a basicFS object, the client
should consider it a *basicFS* object and not something else!
Now, the above FileSystem functor is the *real* interface to the client,
who, as you said, shouldn't be concerned about the actual kind of fs
you're dealing with (What if BasicFS is also a possible filesystem?). If
you want, one can say that FileSystem "subclasses" both the ZipFS and
SystemFS, i.e. a FileSystem is either one *or* the other. The dispatch
code is explicitely written and therefore statically resolvable.

In fact, except for FileSystem, the other functors shouldn't be public. In
SML/NJ the CM compilation system provides a nice "extra" module layer to
provide for such basic very high-level exports/imports. Alternatively,
ML-programmers may actually implement the different kind of file systems
in the functor FileSystem right away. This is when you don't use functors
in the object-type sense, but in the module sense. There is quite some
room for discussion here as to what is approriate. My point was to show
that I do not consider tree-like, inheritance-based OO design good (the
world does not look like a tree). Another point I wanted to make is that
dynamic dispatch is not necessary if the language provides you with the
right mechanisms. Recently, I was again astonished about the complexity of
dynamic dispatching in Java in the presence of casts. Moreover, Java does
something else with methods as with fields. Very weird for functional
programmers where functions are normal values like others.

Disclaimer: you have to know that what I write in these posts is not
thought over, but done "on demand". I beleive some larger-scale experience
with functor-like structures fails. Also, I would like to see how
mixin-like classes (with similar power as ML-functors) do in really large
software designs. To my knowledge this has not been done. Perhaps, dynamic
dispatch is of more use in such an environment, especially if you write
very imperative (something which I hardly do in ML-functors, except if
there are algorithmic efficiency reasons).

Kind Regards,

Simon

Victor B. Putz

unread,
Jun 10, 2002, 11:56:27 PM6/10/02
to
(Note change of subject line to reflect difference in content; the
previous "do we need fpls?" thread was, ah, diverging significantly from
its origins...)

Simon Helsen <hel...@informatik.uni-freiburg.de> writes:

> okay, I see why you think dynamic dispatch is useful. I still beleive it
> is debatable whether the "dynamicness" of the dispatch is a good thing. In
> the ML-functor approach, this problem is easily solved by writing the
> dispatch code yourself. You may say that this is "extra" work. Perhaps,
> but it is strong documentation.

Aha! Okay, we have found a significant Point of Contention, I think,
which is always good to identify because we can talk about it directly.

Inherent in the OO mindset is that "writing the dispatch code yourself"
is a waste of time and effort because it involves repetitive duplication
of the same sort of code. My copy of Bertrand Myer's book probably has
a better discussion of this, but in short in comes down to...

In the previous problem (and to be fair, the actual file systems were
never exported beyond the module, but we'll treat it as if my
description was correct), you suggested ad hoc code of the following
sort (yes, I know you wrote it on-the-fly; this is just to repeat the
gist of it):

> fileName (fs: t, name : string)=
> case fs of
> ZipFS zipfs = zfs.fileName (zipfs, name)
> | SystemFS systemfs = sfs.fileName (systemfs,name)

Good so far as it goes, with a single feature. But note the dispatching
table above. Suppose that the file-system object, instead of having one
feature, had ten? Fifteen? Sixty? (okay, if a class had sixty
features that were all defined with dynamic-dispatch, I'd agree that
something sinister was afoot).

But suppose ten. Do we then need to write ten dispatch tables? With
two file system types, this is not a big deal. But what if I had ten
file systems? The dispatch code, with around 100 different dispatchable
entries, is now starting to take up a fair amount of manual effort--and
thus, in cases like this, dynamic dispatch provides a fast, efficient
way to do this *quickly* (and--admittedly, I haven't tried this in OCaml
or any other efficient FPL--very often in OO languages dynamic-dispatch
is significantly more efficient than case- or if- style dispatch).

So in the case of a situation with one interface but many different
implementations (like my simple file-system approach,
or--perhaps--hierarchical drawing programs where different drawable
objects share a common interface, or games where a game world has many
different types of objects which will be addressed in a uniform manner),
I'm still not convinced that dynamic dispatch isn't the best way to go
about things... *in those situations*.

Of course, inheritance and dynamic dispatch is just a tool. I recently
tried rewriting some legacy C++ code in OCaml (for fun, mostly--simple
stuff like integer cartesian points) and began immediately using OCaml's
OO features, only to find that really the problem worked much better
using a straightforward data type and functions. It probably depends
highly on the problem domain.

> The dispatch
> code is explicitely written and therefore statically resolvable.

At compile-time? For all possible configurations? I should think that
you would still have to analyze the run-type "type" of a filesystem (in
your case statement), which amounts to hand-crafted dynamic dispatch,
albeit of a different sort.

And what about run-time extension of the program? In a dynamic OO
situation, adding a new object type with its own dynamic dispatch table
would be quite viable; I'm not certain how you'd extend a
comparison-based approach except by possibly hooking into an extension
dispatch routine if the standard cases fail.

> Moreover, Java does
> something else with methods as with fields. Very weird for functional
> programmers where functions are normal values like others.

Yes, well... I'm a great fan of OO in principle, but I'm not happy with
the way Java does a great deal of stuff. I think you have a valid point
here.

> Disclaimer: you have to know that what I write in these posts is not
> thought over, but done "on demand".

Understood! And I hope you give me the same consideration--I can say
some remarkably obtuse things. In any case, I shall have to chase down
more information on your mixins/functors points; very interesting.

-->VPutz

Tomasz Zielonka

unread,
Jun 11, 2002, 3:10:29 AM6/11/02
to
Victor B. Putz napisał:

> But suppose ten. Do we then need to write ten dispatch tables? With
> two file system types, this is not a big deal. But what if I had ten
> file systems? The dispatch code, with around 100 different dispatchable
> entries, is now starting to take up a fair amount of manual effort--and
> thus, in cases like this, dynamic dispatch provides a fast, efficient
> way to do this *quickly* (and--admittedly, I haven't tried this in OCaml
> or any other efficient FPL--very often in OO languages dynamic-dispatch
> is significantly more efficient than case- or if- style dispatch).

And very often in FP languages pattern-matching (case-of, match-with) is
significantly more efficient then case- or if- statements in languages
without pattern-matching + static-type-checking + algebraic-datatypes.

Regards,
Tom

--
.signature: Too many levels of symbolic links

Franck Arnaud

unread,
Jun 11, 2002, 5:59:04 AM6/11/02
to
Andreas Rossberg:

> > Just being curious: what's "structural subtyping"? (I do have a hunch,

> It denotes a subtyping relation that is defined by standard inductive
> rules over the structure of types. Unlike e.g. interfaces in Java, where

To the OO crowd it may help explaining it that way (if I got it right):
instead of allowing substitution of classes that inherit from a parent,
you allow substitution of classes that _could_ inherit (that is name
and signature of the routines are compatible). So it could be a superset
of explicit OO typing with inheritance then used exclusively for
implementation purposes.

> Structural subtyping is more flexible because it applies `after the
> fact'.

It is also less flexible in that each (structural) type can only
have one meaning (you cannot distinguish two unrelated types with
the same signature/structure). I'm less clear whether types with some
accidental commonality (same names, maybe some but not all
identical function types) can clash or coexist, but there may be
problems. It also is a global property of a system as far as
I can tell, so that two unrelated and independently well typed
blocks of code may clash once together in the same program. Finally
some other language features possible in an explicit type system
may become difficult or impossible with structural typing (eg if
you apply structural typing to Eiffel, you lose design by
contract as it stands).

That said, it does have advantages.

Joachim Durchholz

unread,
Jun 11, 2002, 6:19:28 AM6/11/02
to
Franck Arnaud wrote:
>
> It is also less flexible in that each (structural) type can only
> have one meaning (you cannot distinguish two unrelated types with
> the same signature/structure).

I suspect that such a conflict just shows that the structural equality
test is just too coarse. If the types are different but structural
equality concludes they are the same, it's no wonder that we get into
trouble :-)

Simon Helsen

unread,
Jun 11, 2002, 6:23:50 AM6/11/02
to
Hi Victor.

On Tue, 11 Jun 2002, Victor B. Putz wrote:

>Inherent in the OO mindset is that "writing the dispatch code yourself"
>is a waste of time and effort because it involves repetitive duplication
>of the same sort of code. My copy of Bertrand Myer's book probably has
>a better discussion of this, but in short in comes down to...

perhaps repetitive, but it forces the programmer, designer to "think"
about it. Mind also that this is an ML-functor solution. In Haskell, you
tackle this with the type-class overloading mechanism. (hmmm, I suppose I
sound like someone who has been beaten in an argument ;-)

Perhaps more to the point. In the kind of things I have written so far
(mostly program transformation stuff), dynamic dispatch of the sort of the
previous posts simply did not happen. Explicit dispatch tables worked out
fine. Also, you have to realise that having HO-functions avoids a lot of
this kind of dynamic dispatch, you simply parametrise a function over
another function of polymorphic type. I haven't thought of how to blend
this well with the functor-approach on your problem. I promise I will when
I have time (I have some hard deadlines coming up)

>But suppose ten. Do we then need to write ten dispatch tables? With
>two file system types, this is not a big deal. But what if I had ten
>file systems? The dispatch code, with around 100 different dispatchable
>entries, is now starting to take up a fair amount of manual effort--and
>thus, in cases like this, dynamic dispatch provides a fast, efficient
>way to do this *quickly* (and--admittedly, I haven't tried this in OCaml
>or any other efficient FPL--very often in OO languages dynamic-dispatch
>is significantly more efficient than case- or if- style dispatch).

Yes, I still think the comparable amount of work is insignificant compared
to the unvisibile magic of dynamic dispatch. As for efficiency, I bet you
that the FPL-solution is *more* efficient, since the dispatch is known at
compile-time, allowing all sorts of optimisations and inlining, which
cannot be done with dynamic dispatch!

>I'm still not convinced that dynamic dispatch isn't the best way to go
>about things... *in those situations*.

well, see above. Again, I come from a world where HO-functions are a fact.
I beleive they may tackle a lot of situations in a very natural way as
well. Again, there is lack of experimentation here.

>Of course, inheritance and dynamic dispatch is just a tool. I recently
>tried rewriting some legacy C++ code in OCaml (for fun, mostly--simple
>stuff like integer cartesian points) and began immediately using OCaml's
>OO features, only to find that really the problem worked much better
>using a straightforward data type and functions. It probably depends
>highly on the problem domain.

that is really what I mean. The question is whether dynamic dispatch is
really necessary in a functional language with algebraic datatypes and
functors. I simply never felt the need... (but, perhaps my designs were
not very good). Also, in big designs, it strikes me that the situations
for dynamic dispatch do not occur that frequently. As for design patterns,
a lot of OO design patterns are covered by standard Functional programming
style.

>At compile-time? For all possible configurations? I should think that
>you would still have to analyze the run-type "type" of a filesystem (in
>your case statement), which amounts to hand-crafted dynamic dispatch,
>albeit of a different sort.

I don't understand this.

>And what about run-time extension of the program? In a dynamic OO
>situation, adding a new object type with its own dynamic dispatch table
>would be quite viable; I'm not certain how you'd extend a
>comparison-based approach except by possibly hooking into an extension
>dispatch routine if the standard cases fail.

this may indeed not work at all. It assumes that you recompile FileSystem
as well. This is reasonable in most cases, but I admit certain application
areas need the ability to add functionality at runtime. I beleive that in
those cases a dynamically typed language may be more approriate (Erlang or
Smalltalk for example).

>Understood! And I hope you give me the same consideration--I can say
>some remarkably obtuse things. In any case, I shall have to chase down
>more information on your mixins/functors points; very interesting.

yes, the mixin-approach as proposed in the following paper does provide
for dynamic dispatch. However, it seems to be quite complex...

http://www.cs.rice.edu/CS/PLT/Publications/popl98-fkf.ps.gz

regards,

Simon

Andreas Rossberg

unread,
Jun 11, 2002, 6:35:46 AM6/11/02
to
"Victor B. Putz" wrote:
>
> Inherent in the OO mindset is that "writing the dispatch code yourself"
> is a waste of time and effort because it involves repetitive duplication
> of the same sort of code.

As I already tried to argue in a different posting, "dynamic dispatch"
is built-in in all FPLs. It is nothing more than applying a first-class
function. For the situation described here the classic "objects are
records of functions" view is sufficient, you won't need functors at
all. What OO really adds to this is only a systematic (but questionable)
way of constructing such records incrementally, namely inheritance (with
its inherent open recursion).

OK, I'm lying. Or rather, oversimplifying. There is a second component
complicating things: typing. First, you may want some form of
"subtyping" between your records - not always, but sometimes it's
useful. This can be achieved by introducing record polymorphism (i.e.
the ability to be polymorphic in the number of fields of a record type).
It's essentially what OCaml's object system is built on (as opposed to
subtyping proper), but it would be nice to have for ordinary records.

Moreover, you may need to store objects of different type in a
collection. In the file system example, you have a list of different
file systems. This requires that you are able to hide the actual types
of the individual objects, i.e. a form of existential quantification
(note that subtyping, resp. subsumption, can be regarded as an automatic
but weak form of existential introduction). One neat implicit way is to
use closures. It works in any functional language but does not scale
well to "binary methods" and other more interesting things (most of
which are, incidently, also a problem in OO).

A more flexible approach that would be very natural in languages with
ML-style module systems is to allow passing modules as first-class
values. Instead of records of functions you would be using modules.
Since modules provide a more powerful typing discipline and particularly
support existential quantification (in the disguise of type
abstraction), this gives you everything you need (and more). "Dynamic
dispatch" is selection from a first-class module in this approach.
Unfortunately, very few systems support such a feature already (Moscow
ML being the only one I'm aware of).

Similarly, type classes can be combined with existential types for the
same effect (although you get much less extra expressiveness). This has
long been available in most Haskell systems.

In summary, most of the important stuff that OO gives you is already
there in functional languages. The main problem is that their type
systems are usually to weak to express more advanced examples. But
instead of stuffing whole new and alien OO systems into functional
languages (which duplicates a lot of machinery) it is much more
promising IMHO to extend their type systems carefully to bridge the
remaining gap. Most of that has been worked out and is available
already, but not yet mainstream.

Ulf Wiger

unread,
Jun 11, 2002, 10:06:43 AM6/11/02
to
>>>>> "-" == Andreas Rossberg <ross...@ps.uni-sb.de> writes:

> A more flexible approach that would be very natural in languages
> with ML-style module systems is to allow passing modules as
> first-class values. Instead of records of functions you would be
> using modules. Since modules provide a more powerful typing
> discipline and particularly support existential quantification
> (in the disguise of type abstraction), this gives you everything
> you need (and more). "Dynamic dispatch" is selection from a
> first-class module in this approach. Unfortunately, very few
> systems support such a feature already (Moscow ML being the only
> one I'm aware of).

Erlang's module system is not quite as sophisticated as ML's, but
the practice of using the module system for dynamic dispatch
(so called "behaviours") is wide-spread and proven to be extremely
useful in large-scale programming.

The most used behaviour is the generic server, where a standard
library implements a full-featured client-server framework, and the
programmer writes a parameterization module to handle application-
specific messages. The module needs to implement a certain set of
handler functions:

init(Argument) -> {ok, State} | _
handle_call(Request, From, State) -> {reply, Reply, State'} | _
handle_cast(Message, State) -> {noreply, State'} | _
code_change(FromVersion, OldState, Xtra) -> {ok, State'}
terminate(Reason, State) -> ok.

Other behaviours can be easily implemented. I once wrote a dynamic
process dispatcher which could administer short-lived threads.
Here's an extract:

handle_cast({new, Mod, Key, Args}, S) ->
Pid = spawn_link(fun() ->
case catch Mod:init(Key, Args) of
{'EXIT', Reason} ->
Mod:terminate(Reason, Key, Args);
_ ->
ok
end
end),
NewS = log_awake(Key, Mod, Pid, S),
{noreply, NewS};

where Mod is a user-supplied module that implements a given
set of functions needed for this behaviour. The dispatcher itself
uses the gen_server behaviour, as implied by the handle_cast()
function.

/Uffe
--
Ulf Wiger, Senior Specialist,
/ / / Architecture & Design of Carrier-Class Software
/ / / Strategic Product & System Management
/ / / Ericsson Telecom AB, ATM Multiservice Networks

Michael Schuerig

unread,
Jun 11, 2002, 10:34:08 AM6/11/02
to
Simon Helsen wrote:

> The question is whether dynamic dispatch is
> really necessary in a functional language with algebraic datatypes and
> functors.

Let's get to a very simple example of runtime polymorphism. It's easy
in Java, but how would it look like in an FPL?

interface Ifc {
void method1();
void method2();
}

class C1 implements Ifc {
public void method1() {
System.out.println("C1.method1");
}
public void method2) {
System.out.println("C1.method2);
}
}

class C2 implements Ifc {
public void method1() {
System.out.println("C2method1");
}
public void method2) {
System.out.println("C2method2);
}
}

public static void main(String[] args) {
Ifc[] l = { new C1(), new C2() };
for (int i = 0; l.length; i++) {
l[i].method1();
l[i].method2();
}
}

The expected output of this program is
C1.method1
C1.method2
C2.method1
C2.method2

This example is, of course, pretty contrived, but the general structure
is *very* common in object-oriented programs. So common, in fact, that
it would be a major pain -- as well as unnatural -- to handle these
cases with explicit dispatch tables. In general, OO programmers are
used to deal with objects as instances of abstract classes (better:
implementing interfaces), without regard to their concrete dynamic
class.

Michael

--
Michael Schuerig GPG Fingerprint
mailto:schu...@acm.org DA28 7DEB 5856 3365 BED9
http://www.schuerig.de/michael/ 8365 0A30 545A 82D2 05D7

Victor B. Putz

unread,
Jun 11, 2002, 11:03:02 AM6/11/02
to
Simon Helsen <hel...@informatik.uni-freiburg.de> writes:

> (hmmm, I suppose I
> sound like someone who has been beaten in an argument ;-)

Heh--I wouldn't say so at all. You just sound like someone with a
different point of view, which is almost always good.

> Yes, I still think the comparable amount of work is insignificant compared
> to the unvisibile magic of dynamic dispatch.

Okay, I think we can probably let that point rest as "difference in
opinion" (I think the haze of maintaining dispatch tables by hand is a
hindrance to software maintenance and extension, you think it is an aid
to comprehension; matter of taste, I guess).

> As for efficiency, I bet you
> that the FPL-solution is *more* efficient, since the dispatch is known at
> compile-time, allowing all sorts of optimisations and inlining, which
> cannot be done with dynamic dispatch!

...


> >At compile-time? For all possible configurations? I should think that
> >you would still have to analyze the run-type "type" of a filesystem (in
> >your case statement), which amounts to hand-crafted dynamic dispatch,
> >albeit of a different sort.
>
> I don't understand this.

Again, I may have misunderstood your original point. What I was getting
at is that you are doing a comparison of the object's type on every
feature call. Now, this may be tremendously fast, and *IF* the type of
the objects is known at compile-time, then certainly this allows "all
sorts of optimizations and inlining."

But does this work if the types of the objects are not known at
compile-time?

Perhaps I'm not understanding correctly here. It sounds as if you're
saying that the system does whole-problem analysis, so that if object A
is declared as a FOO_BLURFL instead of a FOO_BAR (base class FOO), then
the compiler will hardwire calls to object A to automatically use the
FOO_BLURFL features. Which is great... but what if the type of A
depended on user input or some other "random factor"?

(Now if you were saying that a library, say, supported sixteen different
subtypes, but the compiler could detect that only two were possibly
going to be created in a given application and so "weeded" the
comparison tables of unnecessary comparisons, I'll buy that; some OO
compilers do something similar in terms of weeding out dead code;
SmallEiffel comes to mind).


Andreas Rossberg <ross...@ps.uni-sb.de> writes:

> As I already tried to argue in a different posting, "dynamic dispatch"
> is built-in in all FPLs. It is nothing more than applying a first-class
> function.

I think I understand what you are getting at here; let me make sure.
When I read through "Structure and Interpretation of Computer Programs",
I was pretty amazed at how Scheme, a "simple language", was able to
create a flexible object system by storing functions keyed by symbol, so
that you could create objects on the fly (in fact, independent of a
notion of "class", since essentially the objects and their dispatch
tables were created from whole cloth).

If that's what you are getting at, I agree--it's pretty impressive, and
I imagine it does come "free" with most FPLs.

> What OO really adds to this is only a systematic (but questionable)
> way of constructing such records incrementally, namely inheritance (with
> its inherent open recursion).

Again, this may come down to a difference of opinion. I'm still not
sure what's questionable about constructing class hierarchies
independently. To me, it helps descendants in an inheritance tree focus
on their specific problems without having to worry about excess
baggage. I'm not sure that "its inherent open recursion" (I'm assuming
you mean possible inheritance cycles and such) is *really* all that
dangerous in practice. I have not known it to be so.

> Moreover, you may need to store objects of different type in a
> collection.

...


> One neat implicit way is to
> use closures. It works in any functional language but does not scale
> well to "binary methods" and other more interesting things (most of
> which are, incidently, also a problem in OO).

Okay, this is an interesting idea that I hadn't considered (due to my
lack of experience in functional languages). Sorry to pester, but could
you expand on this a bit for me?

> In summary, most of the important stuff that OO gives you is already
> there in functional languages. The main problem is that their type
> systems are usually to weak to express more advanced examples.

Sure. The question, I think, wasn't "can dynamic dispatch be done in
functional languages" (which is obviously true), but rather 1) is it
useful or harmful (and/or in what circumstances), and 2) Is inheritance
useful or harmful (particularly when combined with dynamic dispatch).

-->VPutz

Andreas Rossberg

unread,
Jun 11, 2002, 11:19:34 AM6/11/02
to
Michael Schuerig wrote:
>
> Let's get to a very simple example of runtime polymorphism. It's easy
> in Java, but how would it look like in an FPL?

Come on, that's really too trivial to be meant serious, isn't it? Well,
here you go...

> interface Ifc {
> void method1();
> void method2();
> }

type ifc = {m1 : unit -> unit, m2 : unit -> unit}

> class C1 implements Ifc {
> public void method1() {
> System.out.println("C1.method1");
> }
> public void method2) {
> System.out.println("C1.method2);
> }
> }

fun newC1() = {m1 = fn() => print "C1.m1", m2 = fn() => print "C1.m2"}

> class C2 implements Ifc {
> public void method1() {
> System.out.println("C2method1");
> }
> public void method2) {
> System.out.println("C2method2);
> }
> }

fun newC2() = {m1 = fn() => print "C2.m1", m2 = fn() => print "C2.m2"}

> public static void main(String[] args) {
> Ifc[] l = { new C1(), new C2() };
> for (int i = 0; l.length; i++) {
> l[i].method1();
> l[i].method2();
> }
> }

val l = [newC1(), newC2()]
val _ = List.app (fn x => (#m1 x(); #m2 x())) l

Steven Shaw

unread,
Jun 11, 2002, 11:29:55 AM6/11/02
to
Thanks, that's some great ideas to get me thinking.

"Remi VANICAT" <van...@labri.u-bordeaux.fr> wrote in message
news:ya31ybf...@serveur3.labri.fr...

Steven Shaw

unread,
Jun 11, 2002, 11:42:37 AM6/11/02
to

"Simon Helsen" <hel...@informatik.uni-freiburg.de> wrote in message > I

don't quite understand. Why do you need *dynamic* dispatch? Any even
> relative simple module language can express this. The actual functions are
> bound at compile or link-time. I fail to see the advantage of dynamically
> resolving what method is called. It is dangerous, obscures the code and
> may lead to abuse of the dynamically bound function (i.e.
> spagethi-code). That is my general problem with OO: you end up developing
> large monolithic structures, even at design-level...

If you bind you functions at compile-time you lose flexibility. If you know
exactly beforehand
exactly all the "objects" that will be implementing this interface then
you're right, you don't
need dynamic dispatch. If you don't know, or can't quite be sure, then using
a dynamically
dispatched inteface makes your software extensible.

Why is it dangerous?

I think that it can make the code much more comphrehensible. One interface,
a number of implementations. No manual dispatch code (with the potential to
miss a case for one of the functions
in the interface). Switch statements are what we are asked to look for when
we learn OO programming it points to code that could be turned into
dynamically dispatched code.

Steve.


Brian Rogoff

unread,
Jun 11, 2002, 12:32:30 PM6/11/02
to
On Mon, 10 Jun 2002, Andreas Rossberg wrote:
> BTW, this fuzziness in their tradeoffs is one of the reasons why I
> rarely find good use for objects: usually it seems more consequent to
> either use records or modules. But that may be a problem of my personal
> mindset...

Many of the workarounds for OO designs that are coming up on the
interesting threads concerning OO style vs module/functor style involve
bundling up functions into a record or tuple. In the cases where I've felt
the need to do that, I don't have any problem using OCaml's class system.
One gives up pattern matching, and gains implementation inheritance,
field label overloading, subtyping (sort of, you know what I mean), and
open recursion.

Now, I read what you wrote in the other thread, that with a first class
module system, and type classes, that you could replace object passing
with module passing and field label overloading with type classses. That's
probably true (modulo the open recursion part, which can almost always be
done better with function arguments) but there aren't any languages that
I know about which support those features.

BTW, I think you and others are quite right in that the places where
objects make sense are relatively rare. The core ML features are far more
useful, but I'm not shy about using the OCaml object system as soon as I
start thinking that a record of functions is called for.

> Brian Rogoff wrote:
> > Once you start making modules first class, I begin to wonder why we need
> > to have records and modules,
>
> For very pragmatic reasons. Records should be lightweight and support
> type inference. Modules on the other hand provide a very powerful typing
> discipline which requires explicit annotations and must be carefully
> designed to remain decidable. In particular, they rely on a form of
> dependent typing.

I understand, but first class modules are a bit more heaviweight than
plain old ML modules, which are a bit more heaviweight than Modula-2
style modules. If you argue that there are pragmatic reasons for
separating them, surely similar arguments could be used to justify a
separation of objects (or more polymorphic records, if you prefer) from
the module system, and leaving the module system "second class"?

I'm not trying to be a PITA here, just trying to understand your
reasoning. I'm even looking forward to adding Alice to my list of
favored languages, when you guys add type classes, that is ;-).

> If you consider a language with general dependent types (e.g. Cayenne)
> then there is indeed no point in separating the two (consequently,
> Cayenne doesn't). But then you have to live with undecidable type
> checking and very limited type inference anyway.

That's a topic for a whole 'nother thread, but I suspect that in the
future we'll learn to live with undecidability in our dependently typed
languages.

-- Brian


Michael Schuerig

unread,
Jun 11, 2002, 12:31:18 PM6/11/02
to
Andreas Rossberg wrote:

> Michael Schuerig wrote:
>>
>> Let's get to a very simple example of runtime polymorphism. It's easy
>> in Java, but how would it look like in an FPL?
>
> Come on, that's really too trivial to be meant serious, isn't it?
> Well, here you go...

Depends on your exposure to FP, I guess. I've only a couple of months
ago started to learn Haskell using Thompson's and Hudak's books. I
haven't seen anything like this covered there (so far).

> type ifc = {m1 : unit -> unit, m2 : unit -> unit}

> fun newC1() = {m1 = fn() => print "C1.m1", m2 = fn() => print "C1.m2"}

> fun newC2() = {m1 = fn() => print "C2.m1", m2 = fn() => print "C2.m2"}

> val l = [newC1(), newC2()]
> val _ = List.app (fn x => (#m1 x(); #m2 x())) l

Hm, what I don't understand is, where does it say that objects created
by newCn() are instances of ifc? If I were to extend ifc with another
function, would the compiler tell me to do the same for newCn? How does
the compiler know that every member of l is an instance of ifc? In
particular, (how) does it work in cases where l is not explicitly
given, but instead an argument to a function?

George Caswell

unread,
Jun 11, 2002, 1:10:33 PM6/11/02
to
On Tue, 11 Jun 2002, Andreas Rossberg wrote:

> Michael Schuerig wrote:
> >
> > Let's get to a very simple example of runtime polymorphism. It's easy
> > in Java, but how would it look like in an FPL?
>
> Come on, that's really too trivial to be meant serious, isn't it? Well,
> here you go...
>

(Please remember that some of us, like myself, may be experienced with
various FPLs but not enough so as to know a good way to solve this problem.)

> fun newC2() = {m1 = fn() => print "C2.m1", m2 = fn() => print "C2.m2"}
>

One question: Can m1 and m2 access newC2's namespace? (I presume they
could do newC2().m1() or similar..?)

Thank you for the information.

---GEC
New projects page: http://home.attbi.com/~sieg_haro/
(M-x depeche-mode)
"Must... Finish... Zaku..."

Victor B. Putz

unread,
Jun 11, 2002, 2:01:08 PM6/11/02
to
Michael Schuerig <schu...@acm.org> writes:

> > type ifc = {m1 : unit -> unit, m2 : unit -> unit}
> > fun newC1() = {m1 = fn() => print "C1.m1", m2 = fn() => print "C1.m2"}
> > fun newC2() = {m1 = fn() => print "C2.m1", m2 = fn() => print "C2.m2"}
> > val l = [newC1(), newC2()]
> > val _ = List.app (fn x => (#m1 x(); #m2 x())) l
>
> Hm, what I don't understand is, where does it say that objects created
> by newCn() are instances of ifc?

Assuming the above was MLish, it's done via type inference; the only
type declared so far which had fields named "m1" and "m2" is "ifc",
therefore newCn() must be returning objects of type ifc, and the
compiler will understand this just fine.

Type inference is remarkably powerful and extremely cool. The above
should work fine. But...

> If I were to extend ifc with another
> function, would the compiler tell me to do the same for newCn?

Not directly. But since there will now no longer be a type whose fields
are only m1 and m2, newCn() will give a type error on compilation, I
would imagine.

> How does
> the compiler know that every member of l is an instance of ifc?

Type inference again.

> In
> particular, (how) does it work in cases where l is not explicitly
> given, but instead an argument to a function?

...and again, I would imagine. The basic idea of the example should
work just fine (I don't have a ML compiler installed, just OCaml, which
didn't accept the above without some modification, but the idea seems quite
sound)

-->VPutz

Aaron Denney

unread,
Jun 11, 2002, 3:38:19 PM6/11/02
to
On Wed, 12 Jun 2002 01:42:37 +1000, Steven Shaw <steve...@uq.net.au> wrote:
> No manual dispatch code (with the potential to miss a case for one of
> the functions in the interface).

Haskell and O'Caml will both complain when the pattern-matching does not
cover all cases.

--
Aaron Denney
-><-

Simon Helsen

unread,
Jun 11, 2002, 3:06:52 PM6/11/02
to
On Tue, 11 Jun 2002, Andreas Rossberg wrote:

<andreas's solution to the example with record types>

This problem is not directly solvable with ML Functors as they exist in ML
since there is no such thing as a signature type. I do not think
first-class modules are needed and I actually loath the idea: being able
to play around with modules within the language itself seems dangerous and
would inadvertably lead to spaghetti code. Higher-order functors with a
better "signature" solution would solve a lot of problems. In particular,
one might need signature subtyping (this has been addressed before in this
or another thread I beleive).

In such a context, dynamic dispatch is a non-issue. Every method is always
bound to the object it belongs to, ie. dynamic dispatch is automatic. Your
"runtime polymorphism" comes for free.

The only place where dynamic dispatch *is* an issue (real dynamicness is
involved), is whenever you have the possibility to use 'self' (or 'this'
or whatever). A classical example:

class A : method g = ...
method f = .... self.g ...
class B sub A : method g = ...

an object of class B with invokation of f calls the function bound to B.g,
not A.g. Personally, I cannot possible imagine why anybody would want/need
that, because this is obscurity itself, especially since Java fields
behave differently. And things get really nasty when you add some casts as
well.

Appart from self, dynamic dispatch is not really dynamic and at most a
typing problem (cf Smalltalk, where it is pretty obvious what to do).

Andreas shows how records and first-class functions can be used to achieve
what many OO-people want. I beleive an ML-like functor system with a
different notion of signature would do the job too. The advantage however
that you cannot mix modules and object language as freely as with records
and first-class modules. A defunctorisation at compile-time should be
possible, somehow.

Regards,

Simon

Oleg

unread,
Jun 11, 2002, 6:01:48 PM6/11/02
to
Andreas Rossberg wrote:

> type ifc = {m1 : unit -> unit, m2 : unit -> unit}

> fun newC1() = {m1 = fn() => print "C1.m1", m2 = fn() => print "C1.m2"}

> fun newC2() = {m1 = fn() => print "C2.m1", m2 = fn() => print "C2.m2"}

> val l = [newC1(), newC2()]
> val _ = List.app (fn x => (#m1 x(); #m2 x())) l

Andreas, thanks for a great pedagogical example. I think concrete examples
always work better than abstract discussions. BTW I'm attaching an O'Caml
translation of this at the end for whomever is interested.

How would you "FPLize" the following Employee example adapted from Bjarne
Stroustrup's famous book?

<english>
Employee has a name and department info. When asked to print
itself, it prints them. While Manager is a special type of Employee that
also has level information and a list of pointers to his Employees, unlike
Employee, when asked to print itself, it also prints its level info and
all of its Employees (through Employee::print()). Employees can also be
attach()ed to Managers:
</english>

<c++>
class Employee {
string name;
int department;
public:
Employee(const string& nm, int dept) {
name = nm;
department = dept; } // CONSTRUCTOR
virtual void print() const {
cout << name << "\t" << department << "\n"; }
};

typedef list<Employee*> EL;

class Manager : public Employee {
EL group;
int level;
public:
Manager(const string& nm, int dept, int lvl) : Employee(nm, dept) {
level = lvl; }
void attach(Employee* e) {
group.push_front(e); }
void print_employees() const {
for(EL::const_iterator i = group.begin(); \
i != group.end(); ++i)
(*i)->print(); }
virtual void print () const {
cout << "Manager level " << level << "\n";
Employee::print();
cout << "Responsible for : \n";
print_employees();
cout << "\n";
}
};

int main() {
Employee e1("Brown", 123), e2("Smith", 321);
Manager m("Bush", 123, 2);
m.attach(&e1);

EL l;
l.push_front(&e1);
l.push_front(&e2);
l.push_front(&m);
for(EL::const_iterator i = l.begin(); i != l.end(); ++i)
(*i)->print();
}
</c++>


This pattern demonstrates both inheritance (Manager contains more data than
Employee) and what in C++ is called polymorphism (print() performs
different actions depending on the type). I understand this is the subject
of this thread. The program output is:

----------------------------------------
Manager level 2
Bush 123
Responsible for :
Brown 123

Smith 321
Brown 123
----------------------------------------

This pattern is quite close to a neural net [C++] project I'm working on.
There are different types of neural nets that form a sort of type
hierarchy. Some neural nets are a collection of other [linked] neural nets.
Dynamic dispatch seems necessary and, right now, I can't think of a non-OOP
language suitable for this.

Thanks

Oleg

P.S. Writing this made me notice a limitation of STL containers: if our
list l is list<Employee> instead of list<Employee*>, looping over it will
not display polymorphic behavior (since only pointers are polymorphic in
C++). OTOH if one does use pointers and creates list entries with "new",
they have to be deleted manually.

P.P.S (my O'Caml translation of Andreas's solution)

type ifc = {m1 : (unit -> unit); m2 : (unit -> unit)};;
let newC1 () = {m1 = (fun () -> print_string "C1.m1"); m2 = (fun () ->
print_string "C1.m2")};;
let newC2 () = {m1 = (fun () -> print_string "C2.m1"); m2 = (fun () ->
print_string "C2.m2")};;
let l = [newC1(); newC2()];;
List.map (fun x -> x.m1(); x.m2()) l;;

김 재석/g̊ɪm̚ ʨæsɤk̚/

unread,
Jun 11, 2002, 6:37:21 PM6/11/02
to
George Caswell <sieg...@attbi.com> wrote:
> On Tue, 11 Jun 2002, Andreas Rossberg wrote:
>> fun newC2() = {m1 = fn() => print "C2.m1", m2 = fn() => print "C2.m2"}
>>
> One question: Can m1 and m2 access newC2's namespace? (I presume they
> could do newC2().m1() or similar..?)

IMHO, certainly not. In a sight of view,

type ifc = { m1: unit -> unit, m2: unit -> unit }

seems like as interface of OO-languages. However, as ifc is NOT based on

{ m1: unit -> unit }

, to use inheritance, I guess, we need to find another way. Anyway, that
is much interesting example for similar way to function pointer in C.
--
TeX and EMACS go together in perfect harmony.
------------------------------------------------------------------------
This is war against `evil axis'! E-mail account is surname of my name at
the word which means both pillar and mail after major Top-level-domain.

Tomasz Zielonka

unread,
Jun 11, 2002, 7:57:50 PM6/11/02
to
Victor B. Putz napisał:

> (Now if you were saying that a library, say, supported sixteen different
> subtypes, but the compiler could detect that only two were possibly
> going to be created in a given application and so "weeded" the
> comparison tables of unnecessary comparisons, I'll buy that; some OO
> compilers do something similar in terms of weeding out dead code;
> SmallEiffel comes to mind).

I think you are confusing FP style pattern matching with similar
constructs from languages like C (switch, if ... else if ...).

Strong static typing and algebraic datatypes make it possible to
implement pattern matching very efficiently. In most cases there are no
"comparison tables" which must be _scanned_ to find a matching pattern,
the match is resolved in a couple of CPU instructions. As in OOPLs'
dynamic dispatch, computed jumps are used.

Victor B. Putz

unread,
Jun 11, 2002, 8:43:10 PM6/11/02
to
Tomasz Zielonka <t.zie...@students.mimuw.edu.pl> writes:

> I think you are confusing FP style pattern matching with similar
> constructs from languages like C (switch, if ... else if ...).

Hmm... well, I'm familiar with the (excellent!) pattern-matching in EG
OCaml and Haskell, and I'm aware that OCaml at least can be incredibly
efficient.

What I thought was happening was that folks were taking a "static,
everything about the system can be known at compile-time" viewpoint and
wanted to make sure they were considering the "we don't know what type
object X is going to have at runtime" case, that's all.

-->VPutz

Jacques Garrigue

unread,
Jun 11, 2002, 11:37:17 PM6/11/02
to
Hi,

Here is an encoding in core ocaml of your example combining
inheritance and dynamic dispatch.
Note that I don't even use ocaml records, as they are not good at
inheritance (but they are good at overriding, thanks to the "with"
notation).
I've added the types to make it more readable.
You can see that inheritance is possible, and not even very verbose,
but a bit cumbersome. This is why ocaml has also objects.
However one could say that this is no more than a programming pattern.


type 'a employee = (unit -> unit) * 'a

let print (pr, _) = pr ()
val print : 'a employee -> unit

let new_employee nm dept : _ employee =
let myprint () = print_endline (nm ^ "\t" ^ string_of_int dept) in
(myprint, ())
val new_employee : string -> int -> unit employee

type 'a manager = ((unit employee -> unit) * (unit -> unit) * 'a) employee

let attach (_, (at, _, _) : _ manager) (emp, _ : _ employee) = at (emp, ())
val attach : 'a manager -> 'b employee -> unit

let print_employees (_, (_, pr, _) : _ manager) = pr ()
val print_employees : 'a manager -> unit

let make_manager nm dept lvl self : _ manager =
let employee = new_employee nm dept in
let group = ref [] in
let myprint () =
print_endline ("Manager level " ^ string_of_int lvl);
print employee;
print_endline "Responsible for :";
print_employees (!self ())
and myattach empl =
group := empl :: !group
and myprint_employees () =
List.iter print !group
in
(myprint, (myattach, myprint_employees, ()))
val make_manager :
string -> int -> int -> (unit -> 'a manager) ref -> unit manager

let new_manager nm dept lvl =
let self = ref (fun () -> failwith "self not yet ready") in
let manager = make_manager nm dept lvl self in
self := (fun () -> manager);
manager
val new_manager : string -> int -> int -> unit manager


Oleg <oleg_i...@yahoo.com> writes:

---------------------------------------------------------------------------
Jacques Garrigue Kyoto University garrigue at kurims.kyoto-u.ac.jp
<A HREF=http://wwwfun.kurims.kyoto-u.ac.jp/~garrigue/>JG</A>

Oleg

unread,
Jun 12, 2002, 1:05:47 AM6/12/02
to
Jacques Garrigue wrote:

> Here is an encoding in core ocaml of your example combining
> inheritance and dynamic dispatch.
> Note that I don't even use ocaml records, as they are not good at
> inheritance (but they are good at overriding, thanks to the "with"
> notation).
> I've added the types to make it more readable.
> You can see that inheritance is possible, and not even very verbose,
> but a bit cumbersome. This is why ocaml has also objects.
> However one could say that this is no more than a programming pattern.

<O'Caml code snipped>

Hello Jacques,

I'm not sure how manager is your example satisfies a "IS-A" relationship
WRT employee. For example, I want to have a list "l" of employees, then I
want to stick a manager in the list "l" among regular employees, then I
want to,

List.map print l;;

so that print displays polymorphic behavior depending on what's in the list
(the way list<Employee*> lets one do it in C++)

My problem is that in your example I can't seem to put both managers and
employees into one list, i.e. they have different types, while in the C++
example Manager IS-A Employee, and one can use Manager anywhere Employee
can be used.

Thanks
Oleg

ol...@pobox.com

unread,
Jun 12, 2002, 1:28:25 AM6/12/02
to
<oleg_i...@yahoo.com> wrote in message news:<ae5ru7$er9$1...@newsmaster.cc.columbia.edu>...

> <english>
> Employee has a name and department info. When asked to print
> itself, it prints them. While Manager is a special type of Employee that
> also has level information and a list of pointers to his Employees, unlike
> Employee, when asked to print itself, it also prints its level info and
> all of its Employees (through Employee::print()). Employees can also be
> attach()ed to Managers:
> </english>

I'd like to show three solutions: a fully static dispatch, a dynamic
dispatch with 'switch' (pattern-matching actually), and a dynamic
dispatch with 'virtual functions'. All three solutions are in Haskell;
the third alternative is also implemented in an assignment-free C++.

Solution 1: Dynamic dispatch

type Name = String
type Department = Int
type Level = Int

class EMPLOYEE a

class (EMPLOYEE b) => GROUP a b where
add:: a -> b -> a

data Employee = Employee Name Department

instance EMPLOYEE Employee

instance Show Employee where
show (Employee name dept) = (show name) ++ "\t" ++ (show dept) ++ "\n"

employee = Employee -- the constructor


data Manager = Manager Employee Level Group

instance EMPLOYEE Manager

instance Show Manager where
show (Manager empl level list) = "Manager level " ++ (show level) ++
": " ++ (show empl) ++
"Responsible for: " ++ (show list)


newtype Group = Group [Either Employee Manager]

empty_group = Group []

-- Here's a switch-like dynamic dispatch

instance GROUP Group Employee where
add (Group group) emp = Group $ (Left emp):group

instance GROUP Group Manager where
add (Group group) man = Group $ (Right man):group

instance Show Group where
show (Group []) = ""
show (Group (Left x:tail)) = show x ++ (show $ Group tail)
show (Group (Right x:tail)) = show x ++ (show $ Group tail)

-- the constructor
manager name dept level = Manager (employee name dept) level empty_group

attach (Manager name level emp_list) employee
= (Manager name level (add emp_list employee))

main = do
let e1 = employee "Brown" 123
let e2 = employee "Smith" 321
let m = manager "Bush" 123 2 `attach` e1
let lst = empty_group `add` e1 `add` e2 `add` m
print lst

-- Solution 2: Fully-static dispatch

type Name = String
type Department = Int
type Level = Int

class EMPLOYEE a

data Employee = Employee Name Department

instance EMPLOYEE Employee -- an Employee is the EMPLOYEE

instance Show Employee where
show (Employee name dept) = (show name) ++ "\t" ++ (show dept) ++ "\n"

employee = Employee -- the constructor


-- Statically polytypic list -- should be a part of a library
class PLIST a b c | a b->c where
add:: a -> b -> c

data PL_nil = PL_nil
data PL_cons a b = PL_cons a b

instance Show PL_nil where
show _ = ""

instance (Show a, Show b) => Show (PL_cons a b) where
show (PL_cons a b) = (show a) ++ (show b)

instance PLIST PL_nil b (PL_cons b PL_nil) where
add = flip PL_cons

instance PLIST (PL_cons a t) b (PL_cons b (PL_cons a t)) where
add = flip PL_cons

-- End of statically polytypic list


data Manager list_type = Manager Employee Level list_type

instance EMPLOYEE (Manager a) -- A manager is an employee

instance (Show a) => Show (Manager a) where
show (Manager empl level list) = "Manager level " ++ (show level) ++
": " ++ (show empl) ++
"Responsible for: " ++ (show list)


empty_group = PL_nil

-- The rest is exactly the same as in Solution 1

-- The constructor
manager name dept level = Manager (employee name dept) level empty_group

attach (Manager name level emp_list) employee
= (Manager name level (add emp_list employee))

main = do
let e1 = employee "Brown" 123
let e2 = employee "Smith" 321
let m = manager "Bush" 123 2 `attach` e1
let lst = empty_group `add` e1 `add` e2 `add` m
print lst

Solution 3.
See
http://pobox.com/~oleg/ftp/Computation/Subtyping/Shapes/
http://pobox.com/~oleg/ftp/Computation/Subtyping/Preventing-Trouble.html#ADT

for a superset of the Employee/Manager example. The superset can account
for promotion and demotion.

It is loading more messages.
0 new messages