UnaryOpFunctionProxy : UnaryOpFunction {

	valueFuncProxy { arg args;
		^this.reduceFuncProxy(args)
	}
	reduceFuncProxy { arg args;
		^a.reduceFuncProxy(args).perform(selector)
	}
	value { arg ... args;
		^this.reduceFuncProxy(args)
	}
	valueArray { arg args;
		^this.reduceFuncProxy(args)
	}

	composeUnaryOp { arg aSelector;
		^UnaryOpFunctionProxy.new(aSelector, this)
	}
	composeBinaryOp { arg aSelector, something, adverb;
		^BinaryOpFunctionProxy.new(aSelector, this, something, adverb);
	}
	reverseComposeBinaryOp { arg aSelector, something, adverb;
		^BinaryOpFunctionProxy.new(aSelector, something, this, adverb);
	}
	composeNAryOp { arg aSelector, anArgList;
		^NAryOpFunctionProxy.new(aSelector, this, anArgList)
	}

	// behave like a pattern
	embedInStream { arg inval;
		^this.value.embedInStream(inval)
	}

}

BinaryOpFunctionProxy : BinaryOpFunction {

	valueFuncProxy { arg args;
		^this.reduceFuncProxy(args)
	}
	reduceFuncProxy { arg args;
		^a.reduceFuncProxy(args)
		.perform(selector, b.reduceFuncProxy(args), adverb)
	}
	value { arg ... args;
		^this.reduceFuncProxy(args)
	}
	valueArray { arg args;
		^this.reduceFuncProxy(args)
	}

	composeUnaryOp { arg aSelector;
		^UnaryOpFunctionProxy.new(aSelector, this)
	}
	composeBinaryOp { arg aSelector, something, adverb;
		^BinaryOpFunctionProxy.new(aSelector, this, something, adverb);
	}
	reverseComposeBinaryOp { arg aSelector, something, adverb;
		^BinaryOpFunctionProxy.new(aSelector, something, this, adverb);
	}
	composeNAryOp { arg aSelector, anArgList;
		^NAryOpFunctionProxy.new(aSelector, this, anArgList)
	}

	// behave like a pattern
	embedInStream { arg inval;
		^this.value.embedInStream(inval)
	}

}

NAryOpFunctionProxy : NAryOpFunction {

	reduceFuncProxy { arg args;
		^a.reduceFuncProxy(args).performList(selector, arglist.collect(_.reduceFuncProxy(args)))
	}
	valueFuncProxy { arg args;
		^this.reduceFuncProxy(args)
	}
	value { arg ... args;
		^this.reduceFuncProxy(args)
	}
	valueArray { arg args;
		^this.reduceFuncProxy(args)
	}

	composeUnaryOp { arg aSelector;
		^UnaryOpFunctionProxy.new(aSelector, this)
	}
	composeBinaryOp { arg aSelector, something, adverb;
		^BinaryOpFunctionProxy.new(aSelector, this, something, adverb);
	}
	reverseComposeBinaryOp { arg aSelector, something, adverb;
		^BinaryOpFunctionProxy.new(aSelector, something, this, adverb);
	}
	composeNAryOp { arg aSelector, anArgList;
		^NAryOpFunctionProxy.new(aSelector, this, anArgList)
	}

	// behave like a pattern
	embedInStream { arg inval;
		^this.value.embedInStream(inval)
	}

}

// maybe make it an abstract function object.
NAryValueProxy : NAryOpFunctionProxy {

	*new { arg receiver, args;
		^super.new(nil, receiver, args ? [])
	}
	reduceFuncProxy { arg args;
		^a.reduceFuncProxy(arglist.collect(_.reduceFuncProxy(args)))
	}
	storeOn { arg stream;
		stream << "o(" <<< a << "," <<<* arglist << ")" // is it always so?
	}
}
