Mombu the Programming Forum

Go Back   Mombu the Programming Forum > Programming > record vs. class?
User Name
Password
REGISTER NOW! Mark Forums Read




Reply
1 24th July 10:30
fishifish
External User
 
Posts: 1
Default record vs. class?



Hi, there.
I'm a Java programmer and a newbie in Ocaml.

I'd like to ask you guys opinions about Object-Orientation in Ocaml.

First, the OO feature that I'm looking for in any functional language
is simply 'interface'.
Java has 'extends' keyword to do subclassing or inheritance but in
fact I very seldom use it.
The essential feature in OO, IMHO, is 'interface'. That is, one can
declare and use abstractly the functional specification of a module
without worrying about implementation detail.

In OO community, more and more people realize that inheritance is
replacable with interface aggregation.

With that being said, you can see the 'class' and 'inherit' keyword in
Ocaml are not what a OOer like me is looking for.

The first thing I noted in Ocaml is signature. Undoubtedly it is a
very nice tool. With the type sharing constraint, it sure can do some
advanced thing that Java interface's not capable of. I'm totally in
love with it.
However, it cannot 100% replace the need for 'interface'.
The difference is major:
java interface is first-class type.
A variable can be of type SomeInterface;The return type of a function
can (and normally should) be SomeInterface.

That is not true for signature.


Then I saw 'record'. I almost thought the answer is found.

For example, for a typical Stack example, Java code will be like this:

interface Stack{
void push(int i);
int pop();
bool isEmpty();
}
class StackImpl implements Stack{
...
}

I can write corresponding ocaml code as:

type 'a stack = {push: 'a -> unit; pop: unit -> 'a; isEmpty : unit ->
bool}

let list_stack () = let l = ref [] in
let push x = l := (x :: !l) in
let pop () = let v = List.hd !l in (l := List.tl !l; v) in
let isEmpty () = match !l with [] -> true | _ -> false in
{push=push; pop=pop; isEmpty=isEmpty}
;;
let s1 = list_stack() in s1.push 10; s1.push 20; s1.pop
let s2 = list_stack() in s2.push "hello"; s2.push "world"; s2.pop

record stack is the interface and list_stack is a function that
returns an object that implements stack interface.

It works beautifully.
Plus, the programming-against-interface idiom can be implemented
seamlessly in ocaml now. With a mutable field, method update can also
be implemented with no extra language complexity.

Well, I was wrong. It turned out ocaml record cannot be used to
implement OO because the field names share the same namespace!
Why is that? Is there any technical reason behind this? C provides
individual namespace for each struct. Isn't that more natural?
I know haskell's field selector share the same namespace because they
are just top level functions.
But it looks like ocaml record field names are not treated as top
level functions.

Finally I saw 'class type'. Maybe that is the intended 'interface' in
ocaml? But the # syntax is ugly. More importantly, it looks like the
whole 'class', 'class type' stuff does not integrate with the rest of
the language very well. method update? object combinator? everything
may have to resort to reinvention of wheels.

That's what I have to say now. I might come to a conlusion too soon. I
appreciate anyone who points out my mistakes.
  Reply With Quote


 


2 24th July 10:30
julien signoles
External User
 
Posts: 1
Default record vs. class?



Hi,

It seems that you confuse different concepts. A java interface is not an
ocaml interface and, morever, ocaml has object-oriented features. In
ocaml, you distinguish 3 parts in the language : the core language, the
module language and the OO language. Interface is part of the module
language. You can read the introduction in the manual here :

http://caml.inria.fr/ocaml/htmlman/index.html


In OO community, many people confuse "inheritance" and "subtyping".
Perhaps more and more people realize that "inheritance" is not "subtyping"
;-).


"class" and "inherit" keywords are part of the OO language and there are
what you have in java. But, in ocaml, you don't use them a lot because it
is not the spirit of the language. Instead, you usualy use the module
language.


You're right :-).

You're also right ;-).


It is not the major difference. Java interfaces/classes provide
"inheritance" (not "subtyping") and "late binding". Ocaml interfaces do
not.


In ocaml, you write:

module type STACK = sig
type t
val push : int -> t -> unit
val pop : t -> int
val is_empty : t -> bool
end

module Stack : STACK = struct ... end

This module is part of of the ocaml standard library :

http://caml.inria.fr/ocaml/htmlman/libref/Stack.html

It works but it is not beautiful at all, sorry :-). I suggest you read the
implementation of stacks in the source code of Objective Caml. The code
works beautifully ;-).

"class type" and the # syntax are parts of the OO language. In java and
ocaml, a class defines a (class) type and a class. But in ocaml, it is
possible to only define a class type.

Hope this helps,
Julien
--
mailto:Julien.Signoles@lri.fr ; http://www.lri.fr/~signoles
"In theory, practice and theory are the same,
but in practice they are different" (Larry McVoy)
  Reply With Quote


 


3 10th August 10:18
fishifish
External User
 
Posts: 1
Default record vs. class?


I probably am confused. Actually I'm still trying to understand the OO
part of ocaml. The coersion rules have already lost me. :<


That's what I mean. "inheritance" is not so critical to some OOers
like me. 'subtyping' is.
In java, subtyping is really about interfaces. (java subtyping is
nominal, while ocaml is structural)
And to me, I can't see why I need the whole OO ocaml language if I can
get a per-record namespace for record fields.
The stack example I gave shows how I will seperate implementation from
interface by using record. I think that's enough OO feature for me.

how do I use abstract factory pattern with module language?
A module can be a parameter of another module, but how do I return a
module?
A module cannot be a parameter or return type of a function, that
looks a major limitation to me.


I don't care too much about "inheritance". it's useful, but not
critical.
If you are familiar with java, I'm looking for 'interface' and
'implements'.
Not 'class' and 'extends'.
I assume ocaml signature can do late binding though.
With local modules, I can bind at runtime.

It is a simple sample to show a possible solution to have java
'interface'.
The point is not how to implement "stack", it is how to get
first-class java 'interface'.


Also, it might be relatively trivial, I like the syntax 'mystack.put
1' better than 'Stack.put mystack 1'. The latter seems verbose.
'Stack' and 'mystack' are redundant information.


Java has interface, which is a 'type'. And
"programming-against-interface" puts 'class' in a very minor (although
essential) position.

Thanks a lot. :->
  Reply With Quote
4 10th August 10:19
dave benjamin
External User
 
Posts: 1
Default record vs. class?


I've run into the same dilemma with colliding record names. There are a few
options, but none of them are exactly ideal. You could use name prefixes,
but that gets very redundant. You could put each record type in its own
module, but then you have to use "recordname.ModuleName.fieldname" syntax,
which is also redundant. Or, you can use objects, but then you sacrifice the
ability to do pattern-matching. If you can live without pattern matching,
OCaml 3.08 has a new "anonymous object" syntax:

# let point = object method x = 5 method y = 6 end;;
val point : < x : int; y : int > = <obj>
# point#x;;
- : int = 5
# point#y;;
- : int = 6

This is roughly equivalent to:

let point = {x=5; y=6};;

except that you get a polymorphic record, and you don't have to define any
types (or classes) beforehand. It's not the most concise way of defining a
piece of data, but it's not bad.

Another approach would be to use polymorphic variants--I've been
experimenting with this a little bit:

# let point = `X 5, `Y 6;;
val point : [> `X of int ] * [> `Y of int ] = (`X 5, `Y 6)

The above statement defines a tuple of tagged values. You can use pattern
matching to operate on "point" like this:

# let move (`X x, `Y y) dx dy = `X (x + dx), `Y (y + dy);; val move :
[ `X of int ] * [ `Y of int ] ->
int -> int -> [> `X of int ] * [> `Y of int ] = <fun>
# move point 10 10;;
- : [> `X of int ] * [> `Y of int ] = (`X 15, `Y 16)

However, accomodating methods within such a data structure is too cumbersome to be practical:
# let make_point x y = `X x, `Y y, Move (fun dx dy -> `X (x + dx), `Y (y + dy));; val make_point :
int ->
int ->
[> `X of int ] * [> `Y of int ] *
[> `Move of int -> int -> [> `X of int ] * [> `Y of int ] ] = <fun>
# let point = make_point 4 5;; val point :
[> `X of int ] * [> `Y of int ] *
[> `Move of int -> int -> [> `X of int ] * [> `Y of int ] ] =
(`X 4, `Y 5, `Move <fun>)
# match point with (_, _, `Move f) -> f 10 10;;
- : [> `X of int ] * [> `Y of int ] = (`X 14, `Y 15)

The syntax of the above method call leaves a bit to be desired, eh? =)
That's as far as I've gotten down that path...


I've been reluctantly admitting this as well.

--
.:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
"talking about music is like dancing about architecture."
  Reply With Quote
5 10th August 10:19
andreas rossberg
External User
 
Posts: 1
Default record vs. class?


Indeed. But where do you *really* need to return a module, instead of
just a value of some type defined in a module?


Well, you have 'include'.


Oh, there is no difference in Java between class and interface, and
'inherits' and 'implements' - except that interfaces are subject to
certain restrictions compared to (abstract) classes, in order to avoid
problems with multiple inheritence.

OCaml resolves the MI issues differently, so it does not need this
artificial distinction in its object system.

Depends on what you mean by "late binding" (it is often used with
varying meanings by different people). Modules surely don't give you
built-in "open recursion", where you can rewire pre-existing calls, like
with inheritence in OO languages.

Also, as you noticed, you cannot pass a local module out of scope, so it is rather restricted.

OCaml's object types (which are independent of classes) are
"first-class", and vastly superior to Java's interfaces in most aspects.
They *are* basically "records with a local namespace". You could
rephrase your stack type as (caveat: untested)
type 'a stack = <push: 'a -> unit; pop: unit -> 'a; isEmpty : unit ->
bool>

let list_stack () =
object
val mutable l = []
method push x = l <- x::l
method pop = let v = List.hd l in (l <- List.tl l; v)
method isEmpty = match l with [] -> true | _ -> false
end

let s1 = list_stack() in s1#push 10; s1#push 20; s1#pop
let s2 = list_stack() in s2#push "hello"; s2#push "world"; s2#pop


Are you sure, 'mystack', really? ;-)

Note that you can use 'open' with modules, leaving you with

put mystack 1

vs.

mystack.put 1

Personally, I find the former much more regular. Elsewhere:


This is a consequence of OCaml's structural object types. Since you
never know the actual class that constructed an object, you cannot
assume anything about it. Except for self.

Cheers,

- Andreas

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

Let's get rid of those possible thingies! -- TB
  Reply With Quote
6 10th August 10:19
fishifish
External User
 
Posts: 1
Default record vs. class?


See the abstract factory post about the Product/ProductFactory
example.

There is. multi-inherit is really trivial.
the key is: interface is separate from impl.

Yes. I guess it is. My question is:
A per-record namespace is enough for me, why bother such a big object
system?
It is quite complex and I don't know how seamlessly it can fit into
the rest of the language.


'open' is not a satisfactory solution. Sometimes namespace gets
overlapped.
name clash can happen easily.

I'll need to study more to understand this.
  Reply With Quote
7 10th August 10:19
fishifish
External User
 
Posts: 1
Default record vs. class?


See the abstract factory post about the Product/ProductFactory
example.

There is. multi-inherit is really trivial.
the key is: interface is separate from impl.

Yes. I guess it is. My question is:
A per-record namespace is enough for me, why bother such a big object
system?
It is quite complex and I don't know how seamlessly it can fit into
the rest of the language.


'open' is not a satisfactory solution. Sometimes namespace gets
overlapped.
name clash can happen easily.

I'll need to study more to understand this.
  Reply With Quote
8 10th August 10:19
jacques garrigue
External User
 
Posts: 1
Default record vs. class?


fishifish@gmail.com (Ben) writes:

Interesting marketing talk :-)
From an implementation point of view, multiple inheritance is
difficult. It can also be confusing for the programmer. So Java
designers decided to do without. But they realized that you sometimes
need two classes to share a feature while not coming from the same
root, and they had to add interfaces to do that, which are a form of
multiple inheritance where only one side is allowed to provide an
implementation.

The fact you can use interfaces to enforce abstraction doesn't mean
that interfaces themselves are abstract. Actually, declaring that a
class implements an interface adds some extra code to this class,
independently of the required methods.
Compare this to ocaml class types. They are just abstract requirements
about a class. They generate no code at all, and you can have an
object belong to a class type, as soon as it satisfies the
requirements, even if its class was defined before the class type.


Well, ocaml already had records, and from a practical point of view it
seemed better to keep them unchanged. The namespace has an advantage:
in ML types are implicit, and this way you can directly deduce the
type of a record from any of its fields.
(The SML approach has some advantages of its own, though)

Ocaml objects can be both simple and complex.
But you _can_ just use them as records with a local namespace, and if
this is enough for you then you need know nothing about the rest of the
object system. Yet you might want to understand a few more features,
like polymorphism through #-types and subtyping, which are really just
properties of record types.

---------------------------------------------------------------------------
Jacques Garrigue Kyoto University garrigue at kurims.kyoto-u.ac.jp
<A HREF=http://wwwfun.kurims.kyoto-u.ac.jp/~garrigue/>JG</A>
  Reply With Quote
9 10th August 10:20
joachim durchholz
External User
 
Posts: 1
Default record vs. class?


That just solves multiple implementation inheritance.

Multiple interface inheritance is nontrivial. It occurs if the
subtype objects "are-an" instance of the supertype in multiple,
fundamentally different ways - think of numeric types, which are
subtypes of the Group type twice, namely once for addition and once for
multiplication. A simpler example is having multiple sort orders, it's
just that this trivial case is too easily handled by separating sorting
from the actual object type to be convincing :-)
I know of no language that handles such cases correctly, partly because
the implementation is difficult, but mostly because language designers
usually see multiple interface inheritance as "just a name clash", not
as a subtyping issue. (There's one notable exception: one of the Oz
language designers (I forgot who) claimed that Oz can handle this
correctly. I'm not deep enough into Oz to really check that out though.)

Regards,
Jo
  Reply With Quote
10 10th August 10:20
andreas rossberg
External User
 
Posts: 1
Default record vs. class?


[Anybody knows why some of my posts to c.l.ml - e.g. the one Ben is
answering to - do not appear on several News servers (including Google)?]

[Moderator: Netnews at CMU has been under repair - that's my best guess
as to cause. I've re-submitted your post - apologies to those getting
duplicates. Anyone else having problems please let me know at
comp-lang-ml-request@cs.cmu.edu. -leaf ]


You achieve no more separation using interfaces than you would using
abstract classes and plain inheritence. As long as you never need
multiple implements/inherits relations in a single class - i.e. MI -
interfaces are not required.

When you take a deeper look at the Java spec you'll find that there is
*absolutely nothing* that interfaces technically give you but classes
don't, except for MI! The whole concept of interfaces in Java is
basically a deception.

OCaml's structural object types OTOH are truly separate from classes:
different classes can produce the same object type without having to be
in any kind of declared inheritence relation. You can define
abstractions over object types without even mentioning classes. That's
why I claimed that:

With this I totally agree: I also would prefer a more light-weight
notion of polymorphic records instead of a full-blown object system.
Still, you can use objects that way and ignore classes altogether. It
should not be more expensive either, only the syntax for creating object
records is slightly more heavy than necessary.

Cheers,

- Andreas

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

Let's get rid of those possible thingies! -- TB
  Reply With Quote
Reply


Thread Tools
Display Modes




666