[comp.lang.c] composition of functions

kkenny@wind.nrtc.northrop.com (Kevin B. Kenny KE9TV) (02/13/91)

Reading all the flamage about composable functions, I am forced to
note that everyone appears to agree:
	(1) Functions are not first-class objects in C
	(2) Composition is a difficult notion to express in C.
The disagreements are on the subject of whether this problem is a
major failing in the language.

With the advent of C++, the question is (almost) moot, as compositions
*are* trivial in the latter language.  The trick is to represent
functions explicitly and use operator() as the means of evaluating
them.  Will all of the arguing parties agree that the following short
program (almost) does the right thing?

[NOTE: The (almost) above is included because I can't define new
operators that act on the existing basic types.  Which is the reason
for the #ifdef NEVER, and the kludge of composing everything with the
`id' function.

It's also somewhat awkward that you can't extract a
`pointer-to-function' out of a Function, but it should be obvious why
not.  A `pointer-to-Function' is ALMOST as useful.]

Anyone for working these into an existing class library? A
Smalltalk-flavored one like NIHCL would be a better choice, since the
functionals could work with the type Object (&) (const Object &const),
and the Function class wuldn't have to be replicated for each
combination of parameter type and return type.  Templates would be
ALMOST as suitable.

Kevin Kenny, KE9TV
   cur           w        d      dis     and p
a     sed f iend  rought   eath     ease      ain.
  bles     r     b       br     and         ag

// -*- C++ -*-

#include <stream.h>

// Base class of composable function

class Function {
public:
  virtual int operator () (int) = 0;
};

// The ordinary composable function: construct around a
// reference-to-function-returning-int.


class OrdinaryFunction : public Function {
private:
  int (&f) (int);
public:
  OrdinaryFunction (int (&fn) (int)) : f (fn) {}
  virtual int operator () (int x) { return f (x); }
};

// A composition (functional form) -- construct around two composable functions

class Composition : public Function {
private:
  Function& first;
  Function& second;
public:
  Composition (Function& two, Function& one) 
    : first (one), second (two) {}
  virtual operator () (int x) { return second (first (x)); }
};

#ifdef NEVER
// I'd like to do this....
Function& operator * (int (&two) (int), int (&one) (int))
{
  return *new Composition (*new OrdinaryFunction (two),
			   *new OrdinaryFunction (one));
}
#endif

// The identity function

static int idem (int x) { return x; }

OrdinaryFunction id (idem);

// Syntactic sugar for compositions

Function& operator * (Function& two, int (&one) (int))
{
  return *new Composition (two, *new OrdinaryFunction (one));
}

Function& operator * (int (&two) (int), Function& one)
{
  return *new Composition (*new OrdinaryFunction (two), one);
}

Function& operator * (Function& two,
				Function& one)
{
  return *new Composition (two, one);
}

// The square function

int square (int x)
{
  return x * x;
}

// Let's test some things out.

int main (int, char **)
{
  // Make some compositions

  Function& sqsq = id * square * square;
  Function& sqsqsq = id * square * square * square;

  // Try functions and compositions

  cout << "square (2) = " << square (2) << "\n";
  cout << "sqsq (2)   = " << sqsq (2)   << "\n";
  cout << "sqsqsq (2) = " << sqsqsq (2) << "\n";

  // Try creating a composition on the fly.

  cout << "(square * square) (3) = " << (id * square * square) (3) << "\n";
  return 0;
}

bliss@sp64.csrd.uiuc.edu (Brian Bliss) (02/14/91)

>Reading all the flamage about composable functions, I am forced to
>note that everyone appears to agree:
	(1) Functions are not first-class objects in C
	(2) Composition is a difficult notion to express in C.
>The disagreements are on the subject of whether this problem is a
>major failing in the language.

1) i agree with.

2)
   say you have two functions,

   int f (int x);
   int g (int x);

   what's so hard about writing:

   int fg (int x) { return (f(g(x)); }

 ???????????????????

 bb

sw@smds.UUCP (Stephen E. Witham) (02/15/91)

In article <17621@gremlin.nrtc.northrop.com>, kkenny@wind.nrtc.northrop.com (Kevin B. Kenny KE9TV) writes:
> Reading all the flamage about composable functions, I am forced to
> note that everyone appears to agree:
> 	(1) Functions are not first-class objects in C
> 	(2) Composition is a difficult notion to express in C.
> The disagreements are on the subject of whether this problem is a
> major failing in the language.

I don't agree with number two.  I think composing functions in C is pretty
easy and useful.  In fact, I think quite a few others either know this
or would grudgingly admit it, or suspect that it's true.  But the religious
fervor is concentrated on the "first-classness" issue.  

The problems with doing it in C center around:
   o  Composing functions that take and return more than one type.
   o  The impossibility of using composed functions interchangeably with
      regular C functions.
   o  The fact that you have to take care of deallocating things.

My impression from your posting is that C++ can do this stuff a little
more easily than C, but that it still has these problems.  But I can't
read C++, so could you comment on these particular thorns?

--Steve Witham
Not-the-fault-of: SMDS, Inc., Concord, MA

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (02/15/91)

In article <1991Feb13.212250.27437@csrd.uiuc.edu>, bliss@sp64.csrd.uiuc.edu (Brian Bliss) writes:
> 	(2) Composition is a difficult notion to express in C.
> 2)
>    say you have two functions,
>    int f (int x);    int g (int x);
>    what's so hard about writing:
>    int fg (int x) { return (f(g(x)); }

There's nothing hard about it, but it answers the wrong question.
The question was:  how do I write a function
	typedef int i2ifn(int);
	i2ifn compose(i2ifn f, i2ifn g) { /* fill this in */ }
so that compose(f,g)(x) == f(g(x)).  In Scheme, it's trivial:
	(define (compose f g) (lambda (x) (f (g x)) ))
The answer is that there isn't any way of doing it portably in C where
i2ifns are plain ordinary C functions of the indicated type.  There are
various kludges that let you work with things that are not *C* functions,
and there are some machine-specific hacks, but portable plain C, no.
(The best answer is probably to use Scheme->C.)

-- 
Professional programming is paranoid programming