Goswin von Brederlow
Goswin von Brederlow

Reputation: 12332

ocaml class with method accepting derived classes

Consider the following code:

module Proxy = struct
  type 'a t
end

class qObject proxy = object(self : 'self)
  method proxy : 'self Proxy.t = proxy
end

class qWidget proxy = object(self : 'self)
  inherit qObject proxy
  method add : qWidget -> unit = fun w -> ()
  method as_qWidget = (self :> qWidget)
end

class qButton proxy = object(self : 'self)
  inherit qWidget proxy
  method text = "button"
end

let qObject_proxy : qObject Proxy.t = Obj.magic 0
let qWidget_proxy : qWidget Proxy.t = Obj.magic 0
let qButton_proxy : qButton Proxy.t = Obj.magic 0

let qObject = new qObject qObject_proxy
let qWidget = new qWidget qWidget_proxy
let qButton = new qButton qButton_proxy

let () = qWidget#add qWidget
let () = qWidget#add qButton#as_qWidget

That code is well typed and compiles. But qButton must be manually cast to qWidget which I want to eliminate. I want qWidget#add to accept another qWidget or any derived class (like qButton). I thought #qWidget would be the right type for that but that doesn't work:

class qWidget proxy = object(self : 'self)
  inherit qObject proxy
  method add : #qWidget -> unit = fun w -> ()
end

Error: Some type variables are unbound in this type: ...
The method add has type 'c -> unit where 'c is unbound

and

class qWidget proxy = object(self : 'self)
  inherit qObject proxy
  method add : 'a . (#qWidget as 'a) -> unit = fun w -> ()
end

Error: The universal type variable 'a cannot be generalized:
it escapes its scope.

Is there some way around this that I'm not seeing?

Upvotes: 4

Views: 132

Answers (1)

Goswin von Brederlow
Goswin von Brederlow

Reputation: 12332

I found a way around this. The trick is that add does not need an object that is derived from qWidget but only an object that can cast itself to qWidget, as in has the as_qWidget method. That way the necessary cast to qWidget can be pulled into the method without needing the bothersome #qWidget.

class qWidget proxy = object(self : 'self)
  inherit qObject proxy
  method add : 'a . (<as_qWidget : qWidget; ..> as 'a) -> unit
             = fun w -> let w = w#as_qWidget in ()
  method as_qWidget = (self :> qWidget)
end

Upvotes: 5

Related Questions