Version française
Home     About     Download     Resources     Contact us    
Browse thread
Bug? Constraints get ignored in methods
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Goswin von Brederlow <goswin-v-b@w...>
Subject: Re: [Caml-list] Bug? Constraints get ignored in methods
Martin Jambon <martin.jambon@ens-lyon.org> writes:

> Goswin von Brederlow wrote:
>> Hi,
>> 
>> I want to keep a linked list of structures that have a common subset
>> of functionality. I thought this would be a good use of ocaml objects.
>
> It is not a good use of objects. You'll notice this pretty soon as you'll run
> into a variety of problems:
>
> - polymorphism

Works fine without constraint.

> - initialization

Yep, already run into that one. I basically need 2 constructors. But I
solved that.

> - verbosity

???

> - performance

The disk is the slow part. :)

> All of these issues are inexistent if you use records instead of objects for
> the list structure (or just a classic list).
>
> You can still use objects as elements of the list, but the elements would have
> to share the base type, as you know.

You can not put things of different type into a list. So the elements
of the list must be unified to a common type. Since I need virtual
functions that means either a record of closures bound to a reference
of the actual data (ugly) or objects. So let it be objects.

Now with objects as elements of a normal list the problem remains. I
need to coerce them to the base type before calling the function(s)
handling the base type. And in the real use case there is not a simple
list but a more complicated structure (links between objects of state
clean, dirty, pending read, pending write, ...) so this occurs on more
than one place.

> (continued below)
>
>> A base class with the common subset of functionality and methods to
>> link them. And then derived classes for the specific types. Most
>> simplified it looks like this:
>> 
>> # class type base_type = object val mutable next : base_type option method set_next : base_type option -> unit end;;
>> class type base_type =
>>   object
>>     val mutable next : base_type option
>>     method set_next : base_type option -> unit
>>   end
>> 
>> # class base : base_type = object val mutable next = None method set_next n = next <- n end;;
>> class base : base_type
>> 
>> # class foo = object inherit base method foo = () end;;
>> class foo :
>>   object
>>     val mutable next : base_type option
>>     method foo : unit
>>     method set_next : base_type option -> unit
>>   end
>> 
>> # let a = new base in
>>     let b = new foo in
>>       a#set_next (Some (b :> base_type));;
>> - : unit = ()
>> 
>> # let a = new base in
>>     let b = new foo in
>>       a#set_next (Some b);;
>>                        ^
>> Error: This expression has type foo but is here used with type base_type
>>        The second object type has no method foo
>> 
>> This last error isn't nice. I don't want to have to cast the objects
>> all the time. So I thought there must be a better way using
>> polymorphic methods with a constraint. But here is where everything
>> breaks down. First lets look at just the set_next method:
>> 
>> # class type virtual vbase_type = object method virtual set_next : 'a. 'a option -> unit constraint 'a = #vbase_type end;;
>> class type virtual vbase_type =
>>   object method virtual set_next : 'a option -> unit end
>> 
>> # class virtual vbase : vbase_type = object method virtual set_next : 'a. 'a option -> unit constraint 'a = #vbase_type end;;
>> class virtual vbase : vbase_type
>> 
>> # class base = object inherit vbase method set_next _ = () end;;
>> class base : object method set_next : 'a option -> unit end
>> 
>> # let b = new base;;
>> val b : base = <obj>
>> 
>> # b#set_next (Some 1);;
>> - : unit = ()
>> 
>> Huh? That should not work. 1 is not a superset of #vbase_type. The
>> constraint gets completly ignored by ocaml. Adding back the next gives
>> further problems:
>> 
>> # class type virtual vbase_type = object val mutable next : #vbase_type option method virtual set_next : 'a. 'a option -> unit constraint 'a = #vbase_type end;;
>> class type virtual vbase_type =
>>   object
>>     val mutable next : #vbase_type option
>>     method virtual set_next : 'a option -> unit
>>   end
>> 
>> # class virtual vbase : vbase_type = object val mutable next = None method virtual set_next : 'a. 'a option -> unit constraint 'a = #vbase_type end;;
>> class virtual vbase : vbase_type
>> 
>> # class base = object inherit vbase
>>     method set_next n = next <- (n :> vbase_type option) end;;
>>                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> Error: This method has type #vbase_type option -> unit
>>        which is less general than 'a. 'a option -> unit
>> 
>> Again I  blame ocaml for dropping the constraint. Given the constraint
>> the type would be correct.
>> 
>> 
>> 
>> So how do I have to specify the set_next method that any superset of
>> #base_type will be accepted as argument? Or is that a bug in ocaml and
>> my syntax is perfectly fine?
>
>
> I have no idea. It looks way too complicated.
> Use a classic list:
>
>
> class base = ...
> class derived = ... (* inherits base *)
>
> type obj = Base of base | Derived of derived

That would make this module depend on all possible types. And the
different types depend on this module as they have to alter the
location in the list to implement a move-to-front caching algorithm.
That would cause all my types and the list handling to be in one huge
ml file. No way.

Anyway, it currently looks more like this:

class base = ...
class ['a] data = ...

type t1 = M1.t data
type t2 = M2.t data
type t3 = M3.t data
type t3a = M3.A.t data
type t3b = M3.B.t data
...

where t3a/t3b are derived from t3.

> let obj_list = [ Base (new base); Derived (new derived); ... ]
>
> let iter_base f l =
>   List.iter (function Base x -> f x | Derived x -> f (x :> base)) l
>
> let iter_derived f l =
>   List.iter (function Derived x -> f x | Base _ -> ()) l

Now imagine that with 100 types. It really doesn't scale well. Before
I do that I will use (x :> base) in all places calling set_next like
functions.

MfG
        Goswin