Version française
Home     About     Download     Resources     Contact us    
Browse thread
Pure visitor patterns
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Jacques Garrigue <garrigue@m...>
Subject: Re: [Caml-list] Pure visitor patterns
From: Jason Hickey <jyh@cs.caltech.edu>

> I've been trying to write pure visitors (visitors that compute without
> side-effects).  The main change is that a visitor returns a value.
> Here is a (failed) example specification based on having only one kind
> of thing "foo".
> 
>      class type ['a] visitor =
>        object ('self)
>          method visit_foo : foo -> 'a
>        end
> 
>      and foo =
>        object ('self)
>          method accept : 'a. 'a visitor -> 'a
>          method examine : int
>        end
> 
> This fails because the variable 'a escapes its scope in the method  
> accept.
> It can be fixed by breaking apart the mutual type definition.
> 
>      class type ['a, 'foo] visitor =
>        object ('self)
>          method visit_foo : 'foo -> 'a
>        end
> 
>      class type foo =
>        object ('self)
>          method accept : 'a. ('a, foo) visitor -> 'a
>          method examine : int
>        end
> 
> The second form works, but it is hard to use because of the number
> of type parameters needed for the visitor (in general).
> 
> Here are my questions:
> 
>     - Why does 'a escape its scope in the recursive definition?

Because during recursive definitions parameters of these definitions
are handled as monomorphic. So you cannot generalize the 'a locally.

>     - Is there some other style that would solve this problem?

Not really. Using private rows and recursive allow for some more
expressiveness (in particular you can then define pure visitors on
extensible on an extensible collection of classes), but they are a bit
tricky to use in this context, so I'm not sure this is an improvement
for simple cases.

Another trick to make this pattern more scalable is to use constraints
for parameters.

class type ['a, 'cases] visitor =
  object ('self)
    constraint 'cases = <foo: 'foo; bar: 'bar; ..>
    method visit_foo : 'foo -> 'a
    method visit_bar : 'bar -> 'a
  end
class type foo =
  object ('self)
    method accept : 'a. ('a, cases) visitor -> 'a
    method examine : int
  end
and bar =
  object ('self)
    method accept : 'a. ('a, cases) visitor -> 'a
    method examine : bool
  end
and cases = object method foo : foo method bar : bar end

> P.S. Here is an alternate scheme with non-polymorphic visitors, where
> the returned value is just a visitor.  The accept method needs to
> preserve the type, so this one also has the "escapes its scope"
> problem.
> 
>      class type visitor =
>        object ('self)
>          method visit_foo : foo -> 'self
>        end
> 
>      and foo =
>        object ('self)
>          method accept : 'a. (#visitor as 'a) -> 'a
>        end
>      ...

Same reason: #visitor has an hidden type parameter, so it cannot be
generalized in a mutually recursive definition.

Jacques Garrigue