Jump to content
  • Advertisement
Sign in to follow this  
choffstein

Inheritance in O'Caml

This topic is 3605 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am finally buckling down and learning O'Caml. I know SML/NJ, so I am not lost in the functional aspect of the language. Where I am lost is the OO part. ocaml-tutorial.org says that unlike Java's method of inheritance, O'Caml prefers 'hooks'. Can someone clarify what this means, and how they differ? I'm not picking up on any subtle differences with their examples. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
What OCaml-tutorial says is this:

Quote:
I've noticed programmers in Java tend to overuse inheritance, possibly because it's the only reasonable way of extending code in that language. A much better and more general way to extend code is usually to use hooks (cf. Apache's module API). Nevertheless in certain narrow areas inheritance can be useful, and the most important of these is in writing GUI widget libraries.


I understand this as "Inheritance is used a lot in Java because that's the Java way of doing things, but OCaml tends to do many of these things without inheritance. Nevertheless, here's how OCaml does inheritance:"

As a whole, OCaml inheritance has nothing to do with hooks: it works pretty much like Java inheritance (with a few notable exceptions, such as implicit interface implementation, and the possibility of multiple inheritance). Hooking, on the other hand, is just a technique for extending software that is similar to inheritance: instead of extending an entire class and all its methods, the behavior of individual methods is extended instead, at run-time instead of compile-time. It is a limited but lightweight combination of the Chain of Responsibility and Decorator design patterns.

Share this post


Link to post
Share on other sites
As always, a tip of my hat to you good sir. I was having a bit of trouble understanding the distinction, when the examples seemed rather stereotypical OO to me.

Thanks again.

Share this post


Link to post
Share on other sites
Not clear on the hooking thing. Is it something like this (Python)?


class Base:
def func(self): pass

def Derived(): # "constructor" of non-existent Derived class
def _derived_func_impl(self):
print self
result = Base()
# replace base version of function with "override"
result.func = _derived_func_impl
return result

Share this post


Link to post
Share on other sites
Well, hooking isn't replacement: it's addition. So, to implement a hook correctly, you have to add some call to base somewhere in there. The problem is that, since OCaml doesn't have dynamic inheritance models, you can't have inheritance-based hooking.

On the other hand, you can implement hooking in a quite simple fashion: keep a list of existing hooks and, when calling the hook stack, provide the function to call the rest to the top element as a first argument.

let rec call_hook_stack original stack data = 
match stack with
| [] -> original
| h::t -> h (call_hook_stack original t) data


Consider what happens if (as in the Apache example) I'm trying to resolve an URL:

let http_serve_file path = file_contents pqth

let eliminate_private hooks path =
hooks (if is_prefix "/private" path then "/404.html" else path)

let redirect_images hooks path =
hooks (if is_suffix ".jpg" path then "/images"^path else path)

let stack = [ eliminate_private ; redirect_images ]

let resolve_path path = call_hook_stack http_serve_file stack path


I could add new behavior to the resolving system simply by pushing a new element on top of the stack.

Share this post


Link to post
Share on other sites
I am not sure if this example should be regarded as using hooks, but it is possible to create a record type consisting only of partially applied stub functions. The 'with' keyword in a record constructor could then be used to manually achieve overiding of default behaviour (eg of base_entity ).

type entity =
{
noise : unit -> unit ;
location : unit -> int;
}
;;

let base_entity () =
{ noise = ( fun () -> ());
location = ( fun () -> 0 ); } ;;

let make_player () =
let b = base_entity () in
{ b with noise = ( fun () -> print_endline "burp" ) } ;;

let make_dog is_hungry () =
let b = base_entity () in
{ b with noise = ( fun () -> if is_hungry
then print_endline "grrrh (smiling)"
else print_endline "woof" ) } ;;

(* let binding *)
let e = make_player ();;
e.noise();;
let e = make_dog true () ;;
e.noise();;

(* or generic traversal of single entity type *)
let entities = [ make_player (); make_dog true () ; ];;
List.iter ( fun x -> x.noise() ) entities ;;


Edit: ok use source flags to avoid loosing indentation

Share this post


Link to post
Share on other sites
Quote:
Original post by chairthrower
I am not sure if this example should be regarded as using hooks, but it is possible to create a record type consisting only of partially applied stub functions. The 'with' keyword in a record constructor could then be used to manually achieve overiding of default behaviour (eg of base_entity ).


With hooks, the default behavior does something that's necessary, and all the hooks merely alter the parameters or return value of that default behavior—so, if the default behavior isn't called by your hook, then it's not really a hook.

Note that this approach of replacing behavior with the "with" keyword is equally possible, and even easier, using classes:

class base_entity = 
object
method noise = ()
method location = 0
end

class player =
object
inherit base_entity
method noise = print_endline "burp"
end

class dog hungry =
object
val hungry = hungry
method noise = print_endline
(if hungry then "grrrh" else "woof")
end

let player = (new player :> base_entity);;
player # noise;;

let dog = (new dog :> base_entity);;
dog # noise;;

List.iter (fun entity -> entity # noise) [ player ; dog ]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!