Jon's many-language ray tracer

J

Joachim Durchholz

Chris said:
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.

I didn't say that recently, but trying to solve that problem indeed was
one of the major impulses that led me to FP.

Regards,
Jo
 
K

Ketil Malde

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

It doesn't make sense before you define "object". And I suspect the
sentence really means "only objects are first class" and
consequently everything must be shoehorned into objects to be useful.
Come to think of it, a lot of the OO patterns (functor, factory,
visitor...?) seem to be recipes for doing exactly that.

-k
 
C

Chris Rathman

Andreas said:
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.

In mucking with the translations, I've come to think that ML functors
are a better representation of classes and the instantiated objects.
Functors themselves are a sort of class mechanism. The call to the
functor is like the new operation where an environment is cut with
methods and state (aka object).

The hassle with using modules, though, is that modules can not be
passed to functions (you end up having to use functors for everything
to pass around the objects). Personally, I think that if a module
could be used as parameter to functions, there'd be much less need to
create syntactic extensions for OOP constructs. But I'm assuming that
the interaction with the module language has implications when trying
to pass around environments?

Just a thought,
Chris Rathman
 
M

Marcin 'Qrczak' Kowalczyk

Followup-To: comp.lang.functional

Chris Rathman said:
Personally, I think that if a module could be used as parameter to
functions, there'd be much less need to create syntactic extensions
for OOP constructs. But I'm assuming that the interaction with the
module language has implications when trying to pass around
environments?

I think the only reasons why ML distinguishes structures from records
and functors from functions is the type system. With dynamic typing
it's trivial to make them first-class.
 
S

Sam Lindley

I think we have a different definition of what OO means, since much of
The obligatory reference to Rees's list of OO features or
properties:

<http://store.yahoo.com/paulgraham/reesoo.html>

Here's a somewhat different perspective... To me an object is just a special
form of tuple.

I use the following terminology:

member = element of the underlying tuple
method = a member which is a function
class = the type of an object

The only difference between an object and a regular tuple is that the value
of each method is immutable and fixed for all objects of a given class.

A class is given by:
- the type of the underlying tuple
- the fixed values of the methods

I would say a language is object-oriented iff it includes built-in support
for objects as defined above.

Sam
 
M

Marcin 'Qrczak' Kowalczyk

Sam Lindley said:
member = element of the underlying tuple
method = a member which is a function
class = the type of an object

The only difference between an object and a regular tuple is that the value
of each method is immutable and fixed for all objects of a given class.

It's not that simple because... well, the reason can be stated in two ways:
- a method is different depending on which object it's taken from,
- a method has access to the object it's taken from.

Without that, almost all modern languages are OO, and definitely all
functional languages, because they all support records and first-class
functions.

This, and inheritance, are significant complications, and what actually
distinguishes various models of OO.

OO languages typically provide support for changing a method such that
all references to this method from other methods start calling the new
method. It's not that easy...
 
S

Sam Lindley

Marcin 'Qrczak' Kowalczyk said:
It's not that simple because... well, the reason can be stated in two
ways:
- a method is different depending on which object it's taken from,
- a method has access to the object it's taken from.

This still fits my model. An obvious way of encoding such methods is to take
the first argument to be the object. (x.foo(y) is just syntactic sugar for
foo(x, y)). The model also supports static methods - which don't have access
to the object.
Without that, almost all modern languages are OO, and definitely all
functional languages, because they all support records and first-class
functions.

Ah... but that's not enough. How can we enforce the constraint that the
methods are fixed for any given class? Indeed, how can we distinguish
distinct classes, whose underlying record type is the same? It might be
possible to implement a pretty good approximation using dependent types, but
I am doubtful as to whether it's possible in SML or Haskell. In any case, it
wouldn't fit my definition of OO, as it could hardly be called 'built-in
support for objects'. (Of course, any programming construct could be
simulated in any Turing complete language, but that's very different from
providing native support.)
This, and inheritance, are significant complications, and what actually
distinguishes various models of OO.

I see no reason why my model cannot be extended to describe various flavours
of inheritance. The subtyping relation we would obtain would be somewhat
strange - a combination of record subtyping with the additional facility for
subtypes to arbitrarily change some of the methods - but that's not
particularly surprising as subtyping classes is a bit strange.
OO languages typically provide support for changing a method such that
all references to this method from other methods start calling the new
method. It's not that easy...

The OO languages I'm familiar with only allow a method to be changed by
changing the class (i.e. by deriving from it).

Sam
 
M

Matthias Buelow

Sam Lindley said:
The OO languages I'm familiar with only allow a method to be changed by
changing the class (i.e. by deriving from it).

Then you've never seen the powers that are CLOS.

mkb.
 
J

Joachim Durchholz

Sam said:
Here's a somewhat different perspective... To me an object is just a special
form of tuple.

I use the following terminology:

member = element of the underlying tuple
method = a member which is a function
class = the type of an object

The only difference between an object and a regular tuple is that the value
of each method is immutable and fixed for all objects of a given class.

A class is given by:
- the type of the underlying tuple
- the fixed values of the methods

Hmm... there aren't many languages that make types first-class citizens :)

(Not that this would be impossible. I've been toying with that idea for
a few years now.)

Regards,
Jo
 
S

Sam Lindley

Sam Lindley said:
This still fits my model. An obvious way of encoding such methods is to
take the first argument to be the object. (x.foo(y) is just syntactic
sugar for foo(x, y)). The model also supports static methods - which don't
have access to the object.


Ah... but that's not enough. How can we enforce the constraint that the
methods are fixed for any given class? Indeed, how can we distinguish
distinct classes, whose underlying record type is the same? It might be
possible to implement a pretty good approximation using dependent types,
but I am doubtful as to whether it's possible in SML or Haskell. In any
case, it wouldn't fit my definition of OO, as it could hardly be called
'built-in support for objects'. (Of course, any programming construct
could be simulated in any Turing complete language, but that's very
different from providing native support.)


I see no reason why my model cannot be extended to describe various
flavours of inheritance. The subtyping relation we would obtain would be
somewhat strange - a combination of record subtyping with the additional
facility for subtypes to arbitrarily change some of the methods - but
that's not particularly surprising as subtyping classes is a bit strange.

Actually, when I said objects were tuples I should have said records.
Without subtyping, tuples and records are isomorphic (assuming the existence
of a total order on labels), but it's a bit difficult to define record
subtyping on tuples!

Also, plain inheritance just requires record subtyping. It's overriding that
depends on being able to change methods.

Matthias Buelow said:
Then you've never seen the powers that are CLOS.

Correct. I just googled for it and skimmed through Jeff Dalton's brief
guide:

http://www.aiai.ed.ac.uk/~jeff/clos-guide.html

As I understand it, classes in CLOS are first class citizens, and methods
can be changed in classes, but not directly in individual objects (correct
me if I'm wrong). I think all that needs modifying in my model is the
requirement that methods be immutable. They need not be immutable, but they
can only be modified by changing the class.

I'm reminded of an entertaining paper from FOOL 2001 which proposed a
mechanism for metamorphosing objects. The example involved changing a Frog
object into a Prince object :)

Joachim Durchholz said:
Sam Lindley wrote: [..]
A class is given by:
- the type of the underlying tuple
- the fixed values of the methods

Hmm... there aren't many languages that make types first-class citizens
:)

My model doesn't require types to be first-class citizens - unless (as in
CLOS) classes are first-class citizens.



Anyway, here's a revised definition:

An object is a record subject to certain constraints and a class is the type
of an object.

member = field of the underlying record
method = a member which is a function
object = a record such that each method is fixed for all objects of a
given class
class = a pair of
- the type of the underlying record
- the fixed values of the methods

A language is object-oriented iff it includes built-in support for objects.

Sam
 
R

rossberg

This is well-known and there are papers investigating different
encodings of objects along these lines (e.g. by Pierce). Abadi &
Cardelli argued that all these encodings have limits, at least with
respect to subtyping and variance. That was the motivation for their
object calculi.
Ah... but that's not enough. How can we enforce the constraint that the
methods are fixed for any given class? Indeed, how can we distinguish
distinct classes, whose underlying record type is the same?

Counter question: why should we? In fact, I consider such nominal
typing one of the major weaknesses of (most) typed OO languages. It
severely hampers modularity and reuse. Cf. advantages of "duck typing".

- Andreas
 
S

Sam Lindley

This is well-known and there are papers investigating different
encodings of objects along these lines (e.g. by Pierce). Abadi &
Cardelli argued that all these encodings have limits, at least with
respect to subtyping and variance. That was the motivation for their
object calculi.

Good. I'd have been surprised if this idea hadn't been investigated before.
I'd be interested to see what the problems with subtyping and variance
are... One of these days I'll get round to reading up on object calculi.
Counter question: why should we? In fact, I consider such nominal
typing one of the major weaknesses of (most) typed OO languages. It
severely hampers modularity and reuse. Cf. advantages of "duck typing".

I quite agree. Unfortunately, the designers of most mainstream OO languages
appear to see it as a strength.

Sam
 
P

Philippa Cowderoy

I quite agree. Unfortunately, the designers of most mainstream OO languages
appear to see it as a strength.

As do physicists - YMMV. Personally, I've found it useful precisely
because it aids me in using duck typing for refactoring purposes - it lets
me type on what the duck can do rather than what its innards look like.
 
R

rossberg

Philippa said:
Personally, I've found it useful precisely
because it aids me in using duck typing for refactoring purposes - it lets
me type on what the duck can do rather than what its innards look like.

I'm afraid I don't follow. Can you repeat this in less poultry-oriented
terms?

Cheers,

- Andreas
 
J

Joachim Durchholz

Counter question: why should we? In fact, I consider such nominal
typing one of the major weaknesses of (most) typed OO languages. It
severely hampers modularity and reuse.

Counter-counter question: how do we otherwise differentiate between
"real" and "temperature"?
Cf. advantages of "duck typing".

What's that? (URLs are OK.)

Regards,
Jo
 
K

Ketil Malde

What's that? (URLs are OK.)

Presumably this:

http://en.wikipedia.org/wiki/Duck_typing

---[excerpt]---
Duck typing
From Wikipedia, the free encyclopedia.

Duck typing is a humorous way of describing the type non-checking
system typical of some programming languages, such as with Smalltalk,
where the variable value itself determines what it can do. The name
combines two metaphors, both to make a serious point, and to make a
joke.

One could say that the language ducks the issue of typing. Initially
coined by Dave Thomas in the Ruby programming language community, its
premise is that if it walks like a duck, and talks like a duck, then
it is a duck.

Duck typing also refers to the concept in some languages that as long
as an object implements a certain interface, it is interchangeable
with any other object that implements the same interface, no matter
whether the two have a related inheritance hierarchy.
---[excerpt]---

-k
 
A

Andreas Rossberg

Joachim said:
Counter-counter question: how do we otherwise differentiate between
"real" and "temperature"?

Most likely their interface (i.e. their object type) will differ anyway.
For example, temperature certainly has conversion functions for degrees
Celsius and Fahrenheit.

But apart from that, I am not suggesting giving up on type abstraction.
I just suggest that coupling it with object types by requiring *every*
object type to be nominal is a bad idea. For example, I really see no
point in having nominal interface types like in Java. Classes are for
type abstraction, but interfaces aren't. As long as they are nominal you
could as well use classes.
 
J

Joachim Durchholz

Andreas said:
Most likely their interface (i.e. their object type) will differ anyway.
For example, temperature certainly has conversion functions for degrees
Celsius and Fahrenheit.

There are other differences (say, there's no way to assign a meaning to
the product of two temperatures - btw. that's a difference between reals
and any physical type: the product of two types isn't the same type as
the factors, it's of a new type: you can't add the product of two
lengths to a length, but you can add the product of two reals to a real).

I admit it was a bad example :), but I think there are cases where two
types differ but not by signature. For example, when handling
database/TCP/whatever error codes, it makes sense to put each into a
separate type, but the signatures are the same (convert-to-string,
assignment, and comparison).
But apart from that, I am not suggesting giving up on type abstraction.
I just suggest that coupling it with object types by requiring *every*
object type to be nominal is a bad idea. For example, I really see no
point in having nominal interface types like in Java. Classes are for
type abstraction, but interfaces aren't. As long as they are nominal you
could as well use classes.

Not in Java - they are there to provide a substitute for Java's
nonexistent support of multiple inheritance.

Regards,
Jo
 
T

Tomasz Zielonka

[I reordered Joachim's sentences a bit]
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.

I think the ones on http://haskell.org/bookshelf/#monads are quite good.
The problem is that the concept seems to be really difficult to grasp.
This depends on person - I bet there are people who (would) have no
problem with it (I am not one of them).

The important thing is that monads are really easy to use (use existing
ones, create your own ones) once you understand what's going on, and
they are not only a solution for IO in Haskell - they are really useful
as a programming tool.
(The best approximation that I have come up with is "a monad is what's a
pipe in Unix, only type-safe",

If pipes is all you want, using lazy lists and function composition will
be sufficient (and convenient).

I think you are missing a big part of the picture. Simple function
composition is only one of "computation combination strategies" possible
with monads.
but (a) I'm not sure that this is fully correct and

Not even correct for any concrete monad. Unix pipes are severly
constrained as a programming construct. They are only good for simple
processing of a single stream of data. You cannot name and duplicate
intermediate values (well, you could try to save files or fork streams,
but that wouldn't be what you mean by "unix pipes", and still wouldn't
suffice). What about recursion, looping, higher order functions,
closures and various side-effects supported by different monads. If
state is too easy, think about continuations, back-tracking, exceptions
and the ability to mix these effects using monad transformers? Do I want
the state changes to be rolled back when exception happens? Yes? No? No
problem, just compose monad transformers in a correct order.
(b) I'm pretty sure that this still isn't the full picture.)

You are correct.
The topic is vastly overrated.

Given that your understanding of monads is incomplete, I would take your
statement with a grain of salt.
They are just a specific Design Pattern.

One of the best.

Best regards
Tomasz
 
A

alex goldman

BTW, Jon,

can you re-run the benchmark using single precision, and adding
-march=athlon64 (Basically whatever options allow the compiler to use
A64-specific instructions, and keep sizeof(float) = 4)

Also, it would be useful to know L2 cache, bus speed, memory speed. (Some of
these are in /proc/cpuinfo)
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top