1st-class method closures (was Re: Multiple return values) 
Author Message
 1st-class method closures (was Re: Multiple return values)

Quote:

> The only difficulty is to find out where to use functions and where
> objects. Mechanisms like inner classes might bridge this gap if one finds
> a syntax more convenient than Java's.

For the simple case of one function, one could e.g. specify that first-class
usage of a method is equivalent to passing an object of an implicit class
with only a method named "call" with the respective parameter/return types,
and allow structural subtyping at least for this particular case. Thus only
the caller has to decide whether such an abbreviation is used, and remains
free to substitute a suitable full class declaration and object instatiation
later. In a statement-oriented language with a "heavy syntax" (in contrast to
Lisp, where deeply nested expressions are much more orthogonal and pleasant
to use), it might be preferable to still declare local functions explicitly,
and give them a name, to avoid syntactically very complicated expressions.
Of course, in a language like Eiffel, where parameter-less method calls only
consist of the method name, one will have to invent some syntax to refer to
a closure of the method itself, rather than to the result of calling it.
Here's a quick example (which may still leave some questions open) how it may
be done, using the notation { X.F } for a closure over the feature F of X.

deferred class VISITOR[T]
  -- called e.g. to visit the elements of some data structure
feature
  call (x: T) is deferred end
end -- class VISITOR

class BINARY_TREE [ELEMENT]
feature
  traverse_in_order (visit: VISITOR[ELEMENT]) is
      -- Note that this is preferable to CURSOR objects, since we can freely
      -- use recursion. For concurrent traversal of several data structures,
      -- it would be better to have a coroutine for each traversal, which
      -- yields the element to the main code, thus reifying the iterator
      -- only where necessary, and do that mechanically rather than having
      -- the programmer convert recursion into explicit stack management.
      -- The latter may not hurt much for binary trees, but quickly becomes
      -- annoying (and can easily lead to mistakes) for complicated structures.
    do
      if not is_empty then
        left_subtree.traverse_in_order(visit);
        visit(item);
        right_subtree.traverse_in_order(visit);
      end
    end
  -- other features omitted in this example; the intent should be clear
end -- class BINARY_TREE

class STREAM[ELEMENT]
  -- io-stream accepting instances of ELEMENT
feature
  write (x: ELEMENT) is
       -- write x to this stream
    do ... end
  -- ... lots of other useful methods
end -- class FOO

class INTEGER_STREAM_WRITING_VISITOR
  -- this kind of work-around is currently necessary even for relatively
  -- simple cases (see remars at the end of this article).
inherit
  VISITOR[INTEGER]
creation
  make
feature
  call (n: INTEGER) is
    do
      stream.write(n);
    end
feature {NONE}
  stream: STREAM;
  make (s: STREAM[INTEGER]) is
    do
      stream := s;
    end
end -- class INTEGER_STREAM_WRITER

class TEST
feature
  print (n: INTEGER) is
    do
      -- ...
    end
  test (tree: BINARY_TREE[INTEGER]; stream: STREAM[INTEGER]; omit: INTEGER) is
    local
      writing_visitor: INTEGER_STREAM_WRITING_VISITOR;
      foo (x: INTEGER) is
          -- local method, has access to everything to which the outermost
          -- method has access (invent a notation for the enclosing Result)
        do
          if x /= omit then
             print(x); -- implicit reference to `Current'
          end
        end
    do
      tree.traverse_in_order({foo});
        -- use local method `foo'
      tree.traverse_in_order({print});
        -- use method `print' for `Current'
      tree.traverse_in_order({stream.write});
        -- use `write for `stream'
      !!writing_visitor.make(stream); -- assume `stream' isn't assigned again
      tree.traverse_in_order(writing_visitor);
    end
end -- class TEST

Now think about what you had to do if you wanted to simulate such a local
method `foo' in Eiffel, not only as used here, but also such that it could
do whatever a method of TEST could do, including the invocation of features
in other classes with selective export to TEST, and possibly even the
modification of local variables in `test' (which would then have to be
promoted to fields of a shared object accessed both in `test' and in `foo',
rather than just an initialized copy like in INTEGER_STREAM_WRITING_VISITOR).

While technically all this can be done, I'd say such an abbreviation would
be worth having, particularly since the above is only a very simple example,
and much more expressive things can be done if such abstraction facilities
are conveniently available.




Wed, 10 May 2000 03:00:00 GMT  
 1st-class method closures (was Re: Multiple return values)

Correction for the previous posting (the invocation of visit.call(item)
was written as visit(item); probably a Freudian slip showing how much
I'd like to have first-class method closures ;-)

Quote:

> The only difficulty is to find out where to use functions and where
> objects. Mechanisms like inner classes might bridge this gap if one finds
> a syntax more convenient than Java's.

For the simple case of one function, one could e.g. specify that first-class
usage of a method is equivalent to passing an object of an implicit class
with only a method named "call" with the respective parameter/return types,
and allow structural subtyping at least for this particular case. Thus only
the caller has to decide whether such an abbreviation is used, and remains
free to substitute a suitable full class declaration and object instatiation
later. In a statement-oriented language with a "heavy syntax" (in contrast to
Lisp, where deeply nested expressions are much more orthogonal and pleasant
to use), it might be preferable to still declare local functions explicitly,
and give them a name, to avoid syntactically very complicated expressions.
Of course, in a language like Eiffel, where parameter-less method calls only
consist of the method name, one will have to invent some syntax to refer to
a closure of the method itself, rather than to the result of calling it.
Here's a quick example (which may still leave some questions open) how it may
be done, using the notation { X.F } for a closure over the feature F of X.

deferred class VISITOR[T]
  -- called e.g. to visit the elements of some data structure
feature
  call (x: T) is deferred end
end -- class VISITOR

class BINARY_TREE [ELEMENT]
feature
  traverse_in_order (visit: VISITOR[ELEMENT]) is
      -- Note that this is preferable to CURSOR objects, since we can freely
      -- use recursion. For concurrent traversal of several data structures,
      -- it would be better to have a coroutine for each traversal, which
      -- yields the element to the main code, thus reifying the iterator
      -- only where necessary, and do that mechanically rather than having
      -- the programmer convert recursion into explicit stack management.
      -- The latter may not hurt much for binary trees, but quickly becomes
      -- annoying (and can easily lead to mistakes) for complicated structures.
    do
      if not is_empty then
        left_subtree.traverse_in_order(visit);
        visit.call(item);
        right_subtree.traverse_in_order(visit);
      end
    end
  -- other features omitted in this example; the intent should be clear
end -- class BINARY_TREE

class STREAM[ELEMENT]
  -- io-stream accepting instances of ELEMENT
feature
  write (x: ELEMENT) is
       -- write x to this stream
    do ... end
  -- ... lots of other useful methods
end -- class FOO

class INTEGER_STREAM_WRITING_VISITOR
  -- this kind of work-around is currently necessary even for relatively
  -- simple cases (see remars at the end of this article).
inherit
  VISITOR[INTEGER]
creation
  make
feature
  call (n: INTEGER) is
    do
      stream.write(n);
    end
feature {NONE}
  stream: STREAM;
  make (s: STREAM[INTEGER]) is
    do
      stream := s;
    end
end -- class INTEGER_STREAM_WRITER

class TEST
feature
  print (n: INTEGER) is
    do
      -- ...
    end
  test (tree: BINARY_TREE[INTEGER]; stream: STREAM[INTEGER]; omit: INTEGER) is
    local
      writing_visitor: INTEGER_STREAM_WRITING_VISITOR;
      foo (x: INTEGER) is
          -- local method, has access to everything to which the outermost
          -- method has access (invent a notation for the enclosing Result)
        do
          if x /= omit then
             print(x); -- implicit reference to `Current'
          end
        end
    do
      tree.traverse_in_order({foo});
        -- use local method `foo'
      tree.traverse_in_order({print});
        -- use method `print' for `Current'
      tree.traverse_in_order({stream.write});
        -- use `write for `stream'
      !!writing_visitor.make(stream); -- assume `stream' isn't assigned again
      tree.traverse_in_order(writing_visitor);
    end
end -- class TEST

Now think about what you had to do if you wanted to simulate such a local
method `foo' in Eiffel, not only as used here, but also such that it could
do whatever a method of TEST could do, including the invocation of features
in other classes with selective export to TEST, and possibly even the
modification of local variables in `test' (which would then have to be
promoted to fields of a shared object accessed both in `test' and in `foo',
rather than just an initialized copy like in INTEGER_STREAM_WRITING_VISITOR).

While technically all this can be done, I'd say such an abbreviation would
be worth having, particularly since the above is only a very simple example,
and much more expressive things can be done if such abstraction facilities
are conveniently available.




Wed, 10 May 2000 03:00:00 GMT  
 
 [ 2 post ] 

 Relevant Pages 

1. returning multiple values from a method

2. methods & 1st class messages

3. return multiple values from an awk function?

4. OCX Method That Returns a Value

5. ???Eiffel idiom for multiple return values???

6. Tuples, iterators, and multiple return values in Eiffel?

7. Wait ms Timer Multiple returning inaccurate timer value

8. multiple values in a return from function

9. Multiple return values

10. multiple return values

11. Multiple return values

12. Newbie on multiple return values

 

 
Powered by phpBB® Forum Software