Jon's many-language ray tracer

K

Ketil Malde

Andreas Rossberg said:
Right. IMO the defining characteristic of "object-oriented"
programming is structuring programs around autarkic "objects" that
contact each other in some way, but which all decide "by themselves"
how they react to that, in a black-box sort of manner. That's what the
term suggests, and it is the only characteristicum from the above list
which you do /not/ find in other paradigms one way or the other.

FWIW, I've also heard this classified as "object based", while "object
oriented" is taken to include inheritance as well. Seems like a
sensible distinction to me.

-k
 
A

Andreas Rossberg

Ketil said:
FWIW, I've also heard this classified as "object based", while "object
oriented" is taken to include inheritance as well. Seems like a
sensible distinction to me.

Yes, "object-based" was used more frequently in the early nineties(?)
when prototype-based languages were more en vogue, but I haven't heard
it for quite a while. I rather recall the distinction being between
absence and presence of classes. Of course, the latter usually includes
inheritance (I wouldn't try to classify anything via inheritance alone,
because of the even wider confusion about what inheritance is to start
with).
 
M

Marcin 'Qrczak' Kowalczyk

Chris F Clark said:
I think we have a different definition of what OO means, since much of
your description would match what I consider an OO approach, aside for
your persistent instance that certain things are defined separately.

I don't know what should be considered OO.

I do have an opinion about technical characterization of functional
programming:
1. working with immutable values
2. functions are values, including local functions
but not so for OOP. I don't even know whether my language would
qualify as OO.
And, I don't consider that to be relevant as to whether something is OO.

Of course it's not relevant for judging how much we like it, but it
would be nice to have a common terminology to avoid confusion.

I just googled for object-oriented, went to the 10th page (because
first pages seem to be crowded with generic introductions instead of
more real-life usages of the term), and looked at some links:

"The first one was about the report I am writing about the
possibilities of using object-oriented programming in game design."

"Oolex (object-oriented lexer) presents a new idea for lexical
analysis: the scanner is strictly based on the object orientation
paradigm."

I would like to know whether authors mean objects which contain named
functions (the 9th item in Rees's list), which is similar to sending
messages (5), or something else - maybe just relying on dynamic
dispatch.
It's the dispatch to distinct functions which makes something OO,

Perhaps there should be more specific terms to avoid confusion.
I tend to say "traditional OOP" to mean that functions are embedded in
objects, or that an object has a fixed set of messages it responds to,
but I'm afraid it's not sufficiently unambiguous. "Class based OOP"
is too narrow for this if we want to include prototype based OOP.
"Receiver-based OOP"?.

For the wider sense perhaps "relying on dynamic dispatch" would be
enough.
 
C

Chris F Clark

Torben asked:
Have anyone considered a language/environment that allows both kinds
of view of the matrix. I.e., you can switch between the views as you
like?

Yes, this is the direction I am headed (albeit in some limited ways).
For example, in my parser generator, I am working on implementing the
visitor pattern. Now, it is useful to be able to group the visitor
pattern either by AST node or by visiting function. The result is
exactly a matrix. However, my current plans were not to implement it
as such--this exchange is prompting me to reconsider that.

I could see doing so based on another tool that I am working on which
is based on the decision table model. In decision tables, you have a
matrix with columns that represent different choices (things you wish
to consider in your dispatch) and rows that represent actions, things
you wish to happen when the dispatch occurs. The entries in the
matrix represent whether the action happens or not. (It is possible
to use an inheritance model to build the matricies out of simpler
versions.)

You can also use a slightly different representation, similar to
Karnaugh maps, if the dispatch involves a series of questions. The
rows and columns of the matrix are the answers to the questions, and
then entries in the matrix represent specific combinations of the
answers, within the entry are the action (or list of actions) for that
combination.

It is easy enough to switch from one view of the matrix to another,
both are just projections from n-space down to 2-space. Similarly,
one can build the matrix out of "declarations" that [partially] fill
in one or more entries--that's a projection down to 1-space.

If the tool is done right, one could write the declarations
distributed in the manner that was convenient to the developer, and
view the matrix in a variety of formats so that one could find errors
easily.

-Chris
 
J

Joachim Durchholz

Marcin said:
Perhaps there should be more specific terms to avoid confusion.
I tend to say "traditional OOP" to mean that functions are embedded in
objects, or that an object has a fixed set of messages it responds to,
but I'm afraid it's not sufficiently unambiguous. "Class based OOP"
is too narrow for this if we want to include prototype based OOP.

Actually I think that prototype-based languages are "sufficiently
different" to say they are "closely related to OO because they share
many characteristics, but they are a distinct breed despite".

IOW no, I don't think they are OO in the usual sense. Maybe that's
because I never have done serious programming in them ;-)

Regards,
Jo
 
C

Chris F Clark

Andreas said:
I'm aware that some people are using the term OO in a wider sense, but
I don't like it. First - as Rees notes correctly - it practically
renders the term meaningless.

I think the point made on the page is valid:
Perhaps part of the confusion - and you say this in a different way
in your little memo - is that the C/C++ folks see OO as a liberation
from a world that has nothing resembling a first-class functions,
while Lisp folks see OO as a prison since it limits their use of
functions/objects to the style of (9.).

I come from the liberation school, which is my view of what is OO is
wider. Learning OO allowed me whole new ways of modelling things. It
did not render what I had previously learned, e.g. structured
programming invalid. The definition of OO, I learned, was 5 points:
encapsulation, polymorphism, inheritance (and if I recall correctly
abstraction, and dynamic dispatch). Encapsulation and abstraction are
simply "modular programming" and not new to OO. That means for me, OO
was about polymorphism, inheritance, and dynamic dispatch, as those
are the things which "we did not know" prior to OO.

So, yes, I would consider pattern matching a form of dynamic dispatch
and thus something OO, albeit a newer and more interesting form than
OO itself (prior to FP languages) provided.

In fact, I don't consider HOF to be an FP innovation (at least not a
modern one), since HOF have been known and desirable for as long as I
can remember. In fact, one of the frustrations with Pascal (a pre-OO
language) was in trying to write HOFs--the argument passing powers
were more a hinderance than a help, which actually helped make C
popular, since it could do HOFs better.

The distinguishing characteristic of modern FP is type inference, a
better way of writing generic functions. This makes it possible to
write HOFs in a type-safe way. That is an innovation. That
distinguishes it from OO. No amount of inheritance based polymorphism
is going to give one the same freedom as type inference.

Modern FP also inherits from vernerable FP (e.g. Lisp) the idea of
applicative programming, i.e. expressions without side-effects. It
also inehrits the idea of returning "large values" (e.g. lists) rather
than only things which fit in a word. However, I would argue that
both of those have been seen as desirable (with the side-effect-free
part perhaps questionably so) for a long time, even by the non-FP
programmers in the Pascal era.

So, if you want to argue that type inference enaables solutions that
can't be expressed by inheritance, get on your soap-box and we will
listen, especially when you have evidence. Similarly, if you can
claim advantages from immutability, let us know. We OO people want to
learn and grow. (Personally, I am most interested in monads, to see
if they are really a boon. Generally, you see GC adopted by OO and
tail-call-optimization will follow suit.) However, if you want to
criticize us for some slavish adherence to some rule, be careful, we
don't consider ourselves slaves. When I consider myself to be a
convert on FP (and I'm tettering as I find certain aspects of mutable
values really convenient), that doesn't mean I will stop considering
myself an OO programmer (nor even a structured programmer). Knowledge
grows by acretion. You don't need to forget what you've learned in
order to learn more (Well, sometimes you do for a short while, but not
universally and not eternally).

And, yes there are purists, people who are slavish devoted to solving
everything one-way. However, much of that purity is motivated by
practical considerations. For example, in a langue where everything
is an object, what one is really asking for is for everything to be
first-class. Something I think most FPers will appreciate. It's a
real pain when "integers" and "objects" have different rules and
one-or-the-other is not first-class.

So, please add closures and pattern matching to the next language I
use. If you give me bignums, I won't complain either. Just don't
take away inheritance if you can avoid it. It really does solve
certain problems in an elegant way that does minimize code. And the
penalty for using it isn't very high if one finds the right hierarchy.

-Chris
 
C

Chris Rathman

IMO the defining characteristic of "object-oriented" programming
is structuring programs around autarkic "objects" that contact each
other in some way, but which all decide "by themselves" how they react
to that, in a black-box sort of manner.

My rule of thumb is that if it can handle the shapes example, then it's
an object oriented programming language. About as objective as any
other definition of what OOP means. The downside is that this puts
BrainF*ck into the OO PL camp. :)

http://www.angelfire.com/tx4/cus/shapes/index.html

(Still mulling how to best do the example for Alice ML).

Chris Rathman.
 
M

Marcin 'Qrczak' Kowalczyk

Chris Rathman said:
My rule of thumb is that if it can handle the shapes example, then it's
an object oriented programming language. About as objective as any
other definition of what OOP means. The downside is that this puts
BrainF*ck into the OO PL camp. :)

http://www.angelfire.com/tx4/cus/shapes/index.html

Why does C# define accessor methods instead of making fields public?

The simplest solution in my language Kogut doesn't use anything
resembling inheritance, and is quite short compared to other entries:

// Shapes

let MoveTo shape newX newY {
shape.x = newX;
shape.y = newY
};

let RMoveTo shape deltaX deltaY {
shape->MoveTo (shape.x + deltaX) (shape.y + deltaY)
};

let Draw (shape!) {};

// Rectangles

type RECTANGLE = Rectangle (var x) (var y) (var width) (var height);

let Draw (rect ! RECTANGLE) {
WriteLine "Drawing a rectangle at:(" rect.x "," rect.y "), \
Width " rect.width ", Height " rect.height
};

// Circles

type CIRCLE = Circle (var x) (var y) (var radius);

let Draw (circ ! CIRCLE) {
WriteLine "Drawing a circle at:(" circ.x "," circ.y "), \
Radius " circ.radius
};

// Try shapes

let scribble = [(Rectangle 10 20 5 6) (Circle 15 25 8)];
Each scribble ?shape {
shape->Draw;
shape->RMoveTo 100 100;
shape->Draw
};

let rect = Rectangle 0 0 15 15;
rect.width = 30;
rect->Draw;
 
C

Chris Rathman

Marcin said:
Why does C# define accessor methods instead of making fields public?

Many of the languages could have been shortened by either directly
accessing the fields or by using automatic accessors (like ruby).
Can't say that I was always consistent, but the thing I was trying to
do was show how the methods and properties interact. For many of the
languages, this meant that the specific example could have been
shorter. It's the most common complaint I get for the various
languages.

Anyhow, the manual accessors help make the example read better when
hopping from language to language, which is more what I was interested
in rather than showing off a particular language's expressiveness.
The simplest solution in my language Kogut doesn't use anything
resembling inheritance, and is quite short compared to other entries:

Thanks for the entry. Not sure I understand how the method dispatch
occurs within Kogut.

Chris Rathman
 
J

Joachim Durchholz

Chris said:
So, yes, I would consider pattern matching a form of dynamic dispatch
and thus something OO, albeit a newer and more interesting form than
OO itself (prior to FP languages) provided.

Ironically, FPLs predate OO by a decade IIRC.
(Lisp: sometime in the 50ies, Simula in the 60ies - I'm too lazy to
check the exact dates, but I'm pretty sure somebody knows them by heart *g*)
In fact, I don't consider HOF to be an FP innovation (at least not a
modern one), since HOF have been known and desirable for as long as I
can remember. In fact, one of the frustrations with Pascal (a pre-OO
language) was in trying to write HOFs--the argument passing powers
were more a hinderance than a help, which actually helped make C
popular, since it could do HOFs better.

The essential point is that you can construct functions at run-time.

This is not possible in Pascal or C, and marginally possible in C++.
The distinguishing characteristic of modern FP is type inference, a
better way of writing generic functions. This makes it possible to
write HOFs in a type-safe way.

HOFs could always be type-safe. The point of having type inference is
that it makes writing type-safe functions easier, because the type
signatures of a HOF can be quite mind-boggling. E.g. in a Pascal-style
syntax, the fold function would look like this:
function fold (function fun (A): B; initial: A; l: list of A): B
Now fold has a very simple signature; imagine a third-order function
that takes a function taking functions as parameters...

No, type inference doesn't make HOFs possible, but it does a large step
towards making them practical. (Parametric polymorphism is the other
important feature.)
Modern FP also inherits from vernerable FP (e.g. Lisp) the idea of
applicative programming, i.e. expressions without side-effects.

Ironically, applicativity was one of the first things that Lisp lost -
with the introduction of RPLACA and friends.
Though the applicative style never got completely lost. Both styles seem
to have coexisted through the years.
So, if you want to argue that type inference enaables solutions that
can't be expressed by inheritance, get on your soap-box and we will
listen, especially when you have evidence.

Soapboxed FPLers typically talk about parametric polymorphism (not type
inference) being the FP equivalent to ad-hoc polymorphism.

Actually most will readily acknowledge that OO-style polymorphism is
slightly more powerful, but they will also say that parametric
polymorphism avoids a whole slew of problems that are typical for
working with OO class hierarchies: dispatching asymmetries (rare but
requires premature design decisions if it occurs), dissemination of
logic across a class hierarchy (a common problem), no good way to
modularise a class from its subclasses (roughly: private is too
restrictive, protected/public exposes too many implementation details).

I have not enough experience with FPLs to say for sure that the argument
holds up in practice, but I have done enough OO class hierarchy design
to find the arguments plausible.
Similarly, if you can claim advantages from immutability, let us know.

Oh, that's really a FAQ, but anyway, here goes:

All problems that can be associated to pointer aliasing go away.
Instantly and without a trace.

There is no need to copy data structures. Since data will never change
when you're not watching, you can "copy" it by creating a new pointer to it.
In practice, FPLs with no mutable data structures don't even have the
concept of a "reference" - all assignment is by reference, since that's
a safe thing to do under immutability, so there's no need to have
by-value copying.

The absence of aliasing problems allow compilers to do a lot of
aggressive high-level optimisations. Every function can be recast as a
macro expansion and vice versa (try that with MIN() in C...), with no
worries about breaking things. (The worries for an FPL compiler writer
are the heuristics under which a function should be inlined...)
(Personally, I am most interested in monads, to see if they are
really a boon.

The topic is vastly overrated. They are just a specific Design Pattern.

Since this particular pattern is extremely abstract, many people waste
far too much time trying to understand the concept. Unfortunately,
nobody with enough knowledge and talent (and interest) has written a
good generally understandable explanation.
(The best approximation that I have come up with is "a monad is what's a
pipe in Unix, only type-safe", but (a) I'm not sure that this is fully
correct and (b) I'm pretty sure that this still isn't the full picture.)
Generally, you see GC adopted by OO and
tail-call-optimization will follow suit.) However, if you want to
criticize us for some slavish adherence to some rule, be careful, we
don't consider ourselves slaves. When I consider myself to be a
convert on FP (and I'm tettering as I find certain aspects of mutable
values really convenient), that doesn't mean I will stop considering
myself an OO programmer (nor even a structured programmer).

I had a somewhat sobering experience, and I don't consider myself an OO
programmer anymore.

The experience was this: I was part of the basic library standardisation
efforts for Eiffel. We tried to capture all relevant aspects of the
STRING class' in the form of preconditions and postconditions. I learned
two lessons there:
1) Doing assertions over containers without higher-order functions, by
direct recursion, is a nightmare and not really worth the effort. (We
had several cases where a function's body was easier to read than the
assertion. The problem would have gone away if we had been able to
directly write things like "forall characters in Current, this-and-that
holds".)
2) One of the dreams in the Eiffel community were that, given a set of
"precise enough" postconditions, the Eiffel compiler would be able to
generate the routine's body. It came as a shock to me to see that such a
postcondition would already be the function's body in an FPL - so why
wait until Eiffel acquires an "implementation inferencer" (which would
be *very* difficult to write since Eiffel doesn't have a formal
semantics and other problems that would make is very difficult to have
anything reliable for that).
3) Mutability makes it entirely impossible to nail down some essential
aspects of the workings of such a class. For example, if you write an
Eiffel loop like
loop
...
"foo".append(something)
...
end
you'll find that the STRING object will be reused, and grow with every
iteration. This isn't what one would expect, but it's perfectly in line
with mutable string semantics: "foo" is an instruction to the compiler
to create a (mutable) STRING object, and the language designer didn't
write it into the specs whether literal strings have to be recreated on
every loop iteration. (He even refused to add such a notice when asked
about the problem, because having to create the string object on every
iteration would be "inefficient".)

Well, of course I still can program in OO languages. It's just that I
don't see much value in inheritance anymore (but I gladly accept the
encapsulation that OO classes bring).
And, yes there are purists, people who are slavish devoted to solving
everything one-way. However, much of that purity is motivated by
practical considerations. For example, in a langue where everything
is an object, what one is really asking for is for everything to be
first-class. Something I think most FPers will appreciate. It's a
real pain when "integers" and "objects" have different rules and
one-or-the-other is not first-class.

Indeed. Though I'm not sure how that translates to FPL usage - I haven't
noticed anything "not quite functional" about FPLs.
So, please add closures and pattern matching to the next language I
use. If you give me bignums, I won't complain either. Just don't
take away inheritance if you can avoid it.

Inheritance makes type inference largely impossible.

If you have a call that says "foo.frobnicate", you don't know which of
the many classes in your system contains the "frobnicate" function
that's meant.
Well, if all "frobnicates" are in descendants of the same base class,
you can indeed do type inference - but what if they are in different
class hierarchies? From reading the program, neither the programmer nor
the compiler could infer what semantics is actually meant.
It really does solve certain problems in an elegant way that does
minimize code. And the penalty for using it isn't very high if one
finds the right hierarchy.

The same can be said for parametric polymorphism :)

The real question is: it parametric polymorphism enough? And if it isn't
and you have to circumvent the type system: are its other advantages
worth the restrictions?

However, I have one indirect evidence that it's indeed enough: All OO
languages that I know have some way to access RTTI, and to circumvent
the type system to work around the expressivity limitations of the type
system. Type casts (checked in better languages) are quite commonplace
and routinely applied by application programmers.
No such mechanism is available in a typical FPL. Well, some FPLs do have
it, but the docs lace such functions with such heavy warnings that
application programmers almost never use them. And it's very rare that a
programmer calls for such a thing here in comp.lang.functional.
To me, this indicates that parametric polymorphism is both powerful
enough and leads to less typing problems.

Regards,
Jo
 
N

Neelakantan Krishnaswami

Have anyone considered a language/environment that allows both kinds
of view of the matrix. I.e., you can switch between the views as
you like?

Yes, but I think it results in very deep changes to a language, and
doing it right would be much harder than might first seem apparent.

So, modularity is the reason we can write nontrivial programs. We can
make new components out of old ones, and then think about the new
components as black boxes. All medium-sized and bigger programs do
this, with varying degrees of support from the language. Modularity
gets formalized with a contextual equivalence theorem; a a pair of
code chunks are equivalent implementations of a module if you can
substitute them into any client program and get equivalent behavior.

Now, when you can "swap" between the views, then what's client, and
what's module *changes* when you swap things around. So the question
of how to do modular programming in such an environment has a lot of
question marks next to it.

The aspect-oriented programming community faces many of the same
issues; aspects can invasively change the implementation of a module,
and the implementor doesn't necessarily know what aspects will be
applied to the module. See

<http://aosd.net/pipermail/discuss_aosd.net/2005-April/001527.html>

for more discussion of this point.
 
S

Stephen J. Bevan

Have anyone considered a language/environment that allows both kinds
of view of the matrix. I.e., you can switch between the views as you
like?

The following is describes something similar :-

@inproceedings
{ Ossher:iccl:1990
, author= "Harold Ossher"
, title= "Multi-Dimensional Organization and Browsing of
Object-Oriented Systems"
, crossref= "iccl:1990"
, pages= "128--135"
, refs= 10
, source= "copy"
, checked= 20001007
, abstract= "This paper describes a two-dimensional organization for
object-oriented systems, and a browser supporting that
organization. The organization provides sites for documenting both
generic functions and object types, allows convenient browsing and
information hiding according to both function and type, and supports
the notion of {\em abstract types}. The paper then describes
extension of the organization and browswer to multiple dimensions
to allow for {\em multi-methods} that are split into separate
implemetnations based on criteria in addition to receiver type.
Inheritance and information hiding in the multi-demensional case are
discussed briefly. The multi-dimensional browser had been
implemented on top of the RPDE environment framework."
}

@proceeedings
{ iccl:1990
, booktitle= "International Conference on Computer Languages"
, year= 1990
}
 
A

Andreas Rossberg

Chris said:
The definition of OO, I learned, was 5 points:
encapsulation, polymorphism, inheritance (and if I recall correctly
abstraction, and dynamic dispatch). Encapsulation and abstraction are
simply "modular programming" and not new to OO. That means for me, OO
was about polymorphism, inheritance, and dynamic dispatch, as those
are the things which "we did not know" prior to OO.

So, yes, I would consider pattern matching a form of dynamic dispatch
and thus something OO, albeit a newer and more interesting form than
OO itself (prior to FP languages) provided.

I rather had first-class functions in mind, and those were known long
before OO. Or if you want something more recent and fancy, Haskell-style
type classes.

IMO, what was really novel in OO at the time was the structured way in
which inheritance and polymorphism were brought in, along with a
programming paradigm embracing them.
In fact, I don't consider HOF to be an FP innovation (at least not a
modern one), since HOF have been known and desirable for as long as I
can remember. In fact, one of the frustrations with Pascal (a pre-OO
language) was in trying to write HOFs--the argument passing powers
were more a hinderance than a help, which actually helped make C
popular, since it could do HOFs better.

Real HOFs means closures, and Pascal did not have them. Closures require
garbage collection and hence had a pretty hard time in the mainstream
for decades. Both technologies were pioneered in FP.
The distinguishing characteristic of modern FP is type inference, a
better way of writing generic functions.

Schemers definitely would disagree very strongly with putting their
language in the old-fashioned corner! ;-)
Modern FP also inherits from vernerable FP (e.g. Lisp) the idea of
applicative programming, i.e. expressions without side-effects.

Yes, still one of the central themes.
It
also inehrits the idea of returning "large values" (e.g. lists) rather
than only things which fit in a word. However, I would argue that
both of those have been seen as desirable (with the side-effect-free
part perhaps questionably so) for a long time, even by the non-FP
programmers in the Pascal era.

Sure, these ideas (including HOFs) already occurred during the
development of Algol and Algol-68, but time wasn't ripe. (But some of
the involved people later became pioneers for FP.)
So, if you want to argue that type inference enaables solutions that
can't be expressed by inheritance, get on your soap-box and we will
listen, especially when you have evidence.

Type inference is really really neat and spares us a lot of tedium. I
wouldn't identify it as a crucial part of FP, though.
Similarly, if you can
claim advantages from immutability, let us know.

Well, I'm not a purist, so I wouldn't argue for banning mutability
altogether. But neither does any FPL, if you look closer. The puristic
ones just chose to reflect and control it in types, e.g. by means of
monads or by linear typing. I have some sympathy for this idea, but as
far as I'm concerned the jury is still out.

On the other hand, the advantages of at least minimizing and localising
state are obvious, and I see a general tendency in that direction not
only in the FP world. For example, I often have to deal with
concurrency, and in its presence mutability is very very hard and
intricate to control and hence has high potential for obscure bugs. Even
worse with distributed programming. It's no coincidence that many
protocols in Internet technology tend to be (almost) stateless. I
believe that general programming will move further in that direction the
more ubiquitous concurrency and distribution become.
For example, in a langue where everything
is an object, what one is really asking for is for everything to be
first-class. Something I think most FPers will appreciate.

Yes, I fully agree. I found that point on the list a bit odd myself.

Cheers,

- Andreas
 
A

Andreas Rossberg

Joachim said:
No, type inference doesn't make HOFs possible, but it does a large step
towards making them practical. (Parametric polymorphism is the other
important feature.)
Indeed.

Actually most will readily acknowledge that OO-style polymorphism is
slightly more powerful

No, it's not. The two are incomparable in expressiveness. But as you
say, parametric polymorphism is simpler and less problematic. And more
extensible.
3) Mutability makes it entirely impossible to nail down some essential
aspects of the workings of such a class. For example, if you write an
Eiffel loop like
loop
...
"foo".append(something)
...
end
you'll find that the STRING object will be reused, and grow with every
iteration. This isn't what one would expect, but it's perfectly in line
with mutable string semantics: "foo" is an instruction to the compiler
to create a (mutable) STRING object, and the language designer didn't
write it into the specs whether literal strings have to be recreated on
every loop iteration. (He even refused to add such a notice when asked
about the problem, because having to create the string object on every
iteration would be "inefficient".)

Incidentally, OCaml with its, um, debatable feature of mutable strings
has exactly the same problem. :(
Inheritance makes type inference largely impossible.

No, inheritance is not a problem. Subtyping is. And it does not strictly
make type inference impossible, only much less practical. You usually
have to give up completeness, i.e. resort to local type inference.

Cheers,

- Andreas
 
A

Andreas Rossberg

Chris said:
My rule of thumb is that if it can handle the shapes example, then it's
an object oriented programming language. About as objective as any
other definition of what OOP means. The downside is that this puts
BrainF*ck into the OO PL camp. :)

http://www.angelfire.com/tx4/cus/shapes/index.html

Yes, and doesn't the size of this table hence make precisely my point,
i.e. that such a broad definition of OO is utterly useless? ;-)
(Still mulling how to best do the example for Alice ML).

I don't hesitate at all to concede that Alice ML is not an
object-oriented language. :)

Having said that, as the page's problem description stands, it is
straightforward to solve with ML modules:

signature SHAPE =
sig
val draw : unit -> unit
val moveTo : int * int -> unit
val rMoveTo : int * int -> unit
end

functor Shape (val x:int val y:int) =
struct
val x = ref x and y = ref y
fun moveTo (x', y') = (x := x'; y := y')
fun rMoveTo (dx, dy) = (x := !x+dx; y := !y+dy)
end

functor Circle (val x:int val y:int val r:int) =
struct
structure Shape = Shape (val x=x val y=y)
open Shape
val r = ref r
fun setRadius r' = r := r'
fun draw () = ...
end

functor Rectangle (val x:int val y:int val w:int val h:int) =
struct
structure Shape = Shape (val x=x val y=y)
open Shape
val ref w and h = ref h
fun setWidth w' = w := w'
fun setHeight h' = h := h'
fun draw () = ...
end

functor DoSomething (S : SHAPE) =
struct
val _ = (S.draw (); S.moveTo (100, 100); S.draw ())
end

structure R = Rectangle (val x=10 val y=20 val w=5 val h=6)
structure C = Circle (val x=15 val y=25 val r=8)

structure _ = DoSomething R
structure _ = DoSomething C

Note that the problem description does not explicitly require shapes to
be first-class ;-). Also note that I even mimicked inheritance.

Now, if you really need shapes first-class, then you need first-class
modules. In Alice ML you can abuse packages for this, but you loose some
static typing (with static first-class modules like in Moscow ML you
wouldn't):

signature RECTANGLE =
sig
include SHAPE
val setWidth : int -> unit
val setHeight : int -> unit
end

signature CIRCLE =
sig
include SHAPE
val setRadius : int -> unit
end

val r = pack R : RECTANGLE
val c = pack C : CIRCLE

fun doSomething p =
let
structure S = unpack p : SHAPE
in
S.draw (); S.moveTo (100, 100); S.draw ()
end

val _ = List.map doSomething [r, c]

Does that suit you?

Caveat: all code untested.

- Andreas
 
C

Chris Rathman

Andreas said:
fun doSomething p =
let
structure S = unpack p : SHAPE
in
S.draw (); S.moveTo (100, 100); S.draw ()
end
val _ = List.map doSomething [r, c]

Does that suit you?

That's kind of the way I've been leaning to get the polymorphism to
work with the dynamic dispatch. Now, if you could do such without have
to pack and unpack them, you'd have an OO language. :)

I'm also thinking that the casting can go both ways, giving Marcin his
downcasting capabilities that he misses in O'Caml.

Chris Rathman.
 
A

Andreas Rossberg

Chris said:
fun doSomething p =
let
structure S = unpack p : SHAPE
in
S.draw (); S.moveTo (100, 100); S.draw ()
end
val _ = List.map doSomething [r, c]

That's kind of the way I've been leaning to get the polymorphism to
work with the dynamic dispatch. Now, if you could do such without have
to pack and unpack them, you'd have an OO language. :)

I see the smiley, but anyway, isn't that merely a syntactic difference?
Where do you draw the line? For example, with OCaml's objects you need
an explicit coercion for this example, which is similar to a pack. You
do not need to unpack, though. Is that the line? What if packages were
statically typed, like in Moscow ML? Then you could consider allowing to
omit the signature annotation at unpack. The line would be even thinner.
Finally you could define some syntactic sugar like:

p.#a == let structure X = unpack p in X.a end

Now the line has vanished, solely by adding syntactic convenience.
I'm also thinking that the casting can go both ways

You mean in my example? Yes, you always can recover the most specific
signature with unpack. That is indeed like a sort of downcast.

- Andreas
 
J

Joachim Durchholz

Andreas said:
No, inheritance is not a problem. Subtyping is.

Agreed. Been typing faster than thinking...
(actually I meant to say "interface inheritance", which is a kind of
subtyping)
And it does not strictly make type inference impossible, only much
less practical. You usually have to give up completeness, i.e. resort
to local type inference.

Is there a short text that outlines the nature of these problems? I find
that my mental model of what subtyping does to type inference is
seriously lacking.

Regards,
Jo
 
A

Andreas Rossberg

Joachim said:
Is there a short text that outlines the nature of these problems? I find
that my mental model of what subtyping does to type inference is
seriously lacking.

Nothing introductory I'm aware of at the moment. But the basic problem
is simple: without subtyping, type inference just amounts to deriving
and solving a system of equations over types. With subtyping, this
becomes a system of inequations, which makes it explode combinatorially,
because you cannot perform much simplification anymore.

Cheers,

- Andreas
 
C

Chris F Clark

I want to thank both Jo and Andreas for their very well constructed
replies. Hopefully, they will correct some very important (but
hopefully small) misunderstandings in my mind.

A good example of this is my referral to parametric polymorphism as
type inference. I should understand that difference, but I have yet
to do enough FP programming myself to cement that in my mind. (I
don't count the small amount of programming I do in lisp as FP,
because I don't really use map or lambda (except as part of defun),
although I don't use replaca either (just cons/append), so I believe
my code is "pure".)

Hopefully, no one took what I was saying as a criticism of FP, in my
process of trying to defend what I thought was good about OO.
IMO, what was really novel in OO at the time was the structured way in
which inheritance and polymorphism were brought in, along with a
programming paradigm embracing them.

Yes, and I could see a criticism of people who get too slavishly
immersed in that OO structuring as a solution to all problems. I must
admit I am close to having that fault. Not working in a language with
closures and a fold function, I tend to structure my solutions
differently, so that the advantages of a fold function are less
apparent. Therefore, it is easy for me to say, I have this nice
solution that works for me, but not see that there might be other
solutions that might be even nicer, if I shifted perspective.

But this is close to my point. There are OO solutions and they are
workable, more workable than I think credit is given to them. Sure,
there may be solutions using parametric polymorphism that are more
elegant in some dimension. However, there are also points of elegance
that are lost by abandoning OO. There are points to be learned by
trying solutions in different spaces. The different perspectives
allow one to see all the different ways of making the code elegant.

I believe there are quotes by both Dijkstra and Knuth extolling the
virtues of at least occassionally programming something in a much more
restrictive environment or paradigm, as it tends to open ones eyes
better. I can remember the one from Deremer, "Jail is liberating."
(There were times that my PL/I programming becamse better, because I
learned COBOL and how it solved certain problems.) In that light,
think there is much to be learned by figuring out how to structure an
OO program elegantly. It forces one to understand certain aspects of
the problem very well and to really think through certain issues. I
think the understanding of those issues is lost if one goes to a
parametric polymorphic solution, because one doesn't have to resolve
them (and they are then not resovled in an elegant fashion).

I think Jo's example of trying to write invariants et. al. for the
Eiffel runtime are most illustrative. If he hadn't tried to solve that
problem (and most of us never face that problem, because we don't write
in a language with contracts), he wouldn't have seen the elegance of
FP. I know that's not quite what he said, but I think the point is
arguable.

-Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top