Discussion:
How would you call this programming construct?
Mirko Vukovic
2019-10-19 03:11:34 UTC
Hello,

As an example, consider a function that adds two numbers.

I want to call it by passing either two numbers, or functions
of zero arguments that evaluate to numbers:

or
or
(add-two (lambda() 2) (lambda () 3))

I will show my implimentation below. My questions are:
- Is there a name for this concept
- Is there a better way of doing it

My evantual goal is to do Monte Carlo runs on such functions.
The argument functions will return values generated by
random number generators.

Here is the definition of add-two:

The arguments can hold either numeric values or functions of
zero arguments that return numeric values"
(let ((a (if (typep a 'function)
(funcall a)
a))
(b (if (typep b 'function)
(funcall b)
b)))
(+ a b)))

Thanks,

Mirko
Robert L.
2019-10-19 06:17:48 UTC
Post by Mirko Vukovic
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or
or
(add-two (lambda() 2) (lambda () 3))
- Is there a name for this concept
- Is there a better way of doing it
My evantual goal is to do Monte Carlo runs on such functions.
The argument functions will return values generated by
random number generators.
The arguments can hold either numeric values or functions of
zero arguments that return numeric values"
(let ((a (if (typep a 'function)
(funcall a)
a))
(b (if (typep b 'function)
(funcall b)
b)))
(+ a b)))
(apply +
(map
(lambda (x) (if (procedure? x) (x) x))
numbers)))

(add 1 (lambda() 3) 5 7 9)
===>
25
2019-10-19 17:23:13 UTC
Post by Mirko Vukovic
- Is there a name for this concept
Polymorphism
Post by Mirko Vukovic
- Is there a better way of doing it
My evantual goal is to do Monte Carlo runs on such functions.
The argument functions will return values generated by
random number generators.
The best way is to generate the random numbers before calling the
function. If for some reason you really need to pass around a value
that means "yield a random number when a value is requested," you can
make things easier to debug and avoid overhead related to allocating
and calling functions by passing around something simple like a
symbol (or maybe a RANDOM-STATE object), and writing a function that
knows what to do with it:

(defun yield-number (x)
(if (eq x 'random)
(random 999999)
x))

Then:

(+ (yield-number a) (yield-number b)))

YIELD-NUMBER can be declared inline.
--
Software engineering services in Los Angeles https://oneofus.la
smh
2019-10-21 09:19:30 UTC
The polymorphism could alternatively be implemented by writing it as a gf with 4 specialized methods.

An entirely different useful generalization could be to code the function to accept an arbitrary number (zero or more) of arguments, like cl:+ does.
Mirko Vukovic
2019-10-21 18:25:43 UTC
Post by smh
The polymorphism could alternatively be implemented by writing it as a gf with 4 specialized methods.
An entirely different useful generalization could be to code the function to accept an arbitrary number (zero or more) of arguments, like cl:+ does.
Agreed that for this case I could implement it as a GF.

However, this is toy problem, and in some cases I may have more
or less arguments.

(I will amend my original post to explain why I was playing with this
construct)
Kaz Kylheku
2019-10-21 03:22:25 UTC
Post by Mirko Vukovic
Hello,
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or
or
(add-two (lambda() 2) (lambda () 3))
What you're doing is implementing lazy evaluation and explicitly
passing the "thunks" needed to make it work, without any syntatic
sugar. Additionally, you have non-functional terms for simple
constants.
Mirko Vukovic
2019-10-21 18:23:45 UTC
Post by Kaz Kylheku
Post by Mirko Vukovic
Hello,
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or
or
(add-two (lambda() 2) (lambda () 3))
What you're doing is implementing lazy evaluation and explicitly
passing the "thunks" needed to make it work, without any syntatic
sugar. Additionally, you have non-functional terms for simple
constants.
I do not follow the "non-functional terms for simple constants"

I see that when I pass two numbers, the call is functional, whereas if
I pass one or more functions, it is not. Is that what you mean?
Kaz Kylheku
2019-10-22 01:27:00 UTC
Post by Mirko Vukovic
Post by Kaz Kylheku
Post by Mirko Vukovic
Hello,
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or
or
(add-two (lambda() 2) (lambda () 3))
What you're doing is implementing lazy evaluation and explicitly
passing the "thunks" needed to make it work, without any syntatic
sugar. Additionally, you have non-functional terms for simple
constants.
I do not follow the "non-functional terms for simple constants"
I see that when I pass two numbers, the call is functional, whereas if
I pass one or more functions, it is not. Is that what you mean?
Yes; I mean you have two representations of 3: at the top level
in the program, we can specify just 3 or the wrappage (lambda () 3).
These substitute for each other, and in the lower evaluation level, they
reduce to the same thing.

Of course (lambda () 3) itself just has "just 3" in it.

But let's think for a moment of a program transformation tool which
generates this stuff from a notation where you don't see the lambdas.
That tool could translate a complex expression EXPR into (lambda ()
EXPR), thereby delaying its evaluation. But for a simple constant,
it might dispense with the lambda wrapping.
Mirko Vukovic
2019-10-21 18:29:17 UTC
Post by Mirko Vukovic
Hello,
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or
or
(add-two (lambda() 2) (lambda () 3))
- Is there a name for this concept
- Is there a better way of doing it
My evantual goal is to do Monte Carlo runs on such functions.
The argument functions will return values generated by
random number generators.
The arguments can hold either numeric values or functions of
zero arguments that return numeric values"
(let ((a (if (typep a 'function)
(funcall a)
a))
(b (if (typep b 'function)
(funcall b)
b)))
(+ a b)))
Thanks,
Mirko
Let me explain why I am going this way:

I want to be able to subject the guts of my function to unit tests,
and then with a minimum of fuss do a Monte Carlo simulation on the
same function.

In this approach, I would use a macro "DEFSFUN name (a b ...)" that
would accept only arguments (no keywords, optional, default values),
and that would expand the arguments and body to the above shown form
that can handle numbers or functions as arguments.

Mirko
smh
2019-10-21 23:39:44 UTC
(declare (dynamic-extent rest))
(add* 0 (car rest) (cdr rest))) ; avoid O^2 repeated &rest list consing
cl-user(15): (defmethod add* (sofar (next function) rest)
(add* (+ sofar (funcall next)) (car rest) (cdr rest)))
cl-user(16): (defmethod add* (sofar (next number) rest)
(add* (+ sofar next) (car rest) (cdr rest)))
cl-user(17): (defmethod add* (sofar (next null) rest) ; NIL ends recursion
sofar)
cl-user(18): (add 1 2 3 4 5 6)
21
cl-user(19): (add 1 2 3 (lambda () 4) 5 6)
21
cl-user(20): (add 1 2 3 (lambda () 4) #c(5.5 0.5) 11/2)
#C(21.0 0.5)
Mirko Vukovic
2019-10-22 00:53:32 UTC
Post by smh
(declare (dynamic-extent rest))
(add* 0 (car rest) (cdr rest))) ; avoid O^2 repeated &rest list consing
cl-user(15): (defmethod add* (sofar (next function) rest)
(add* (+ sofar (funcall next)) (car rest) (cdr rest)))
cl-user(16): (defmethod add* (sofar (next number) rest)
(add* (+ sofar next) (car rest) (cdr rest)))
cl-user(17): (defmethod add* (sofar (next null) rest) ; NIL ends recursion
sofar)
cl-user(18): (add 1 2 3 4 5 6)
21
cl-user(19): (add 1 2 3 (lambda () 4) 5 6)
21
cl-user(20): (add 1 2 3 (lambda () 4) #c(5.5 0.5) 11/2)
#C(21.0 0.5)
Nice example. Thanks!
2019-10-22 18:37:44 UTC
Post by Mirko Vukovic
Post by Mirko Vukovic
Hello,
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or
or
(add-two (lambda() 2) (lambda () 3))
- Is there a name for this concept
- Is there a better way of doing it
My evantual goal is to do Monte Carlo runs on such functions.
The argument functions will return values generated by
random number generators.
The arguments can hold either numeric values or functions of
zero arguments that return numeric values"
(let ((a (if (typep a 'function)
(funcall a)
a))
(b (if (typep b 'function)
(funcall b)
b)))
(+ a b)))
Thanks,
Mirko
I want to be able to subject the guts of my function to unit tests,
and then with a minimum of fuss do a Monte Carlo simulation on the
same function.
If your main use case is to support unit tests, then perhaps the following

(a) If the basic function is implemented as a (default) generic function, you
could add an :AROUND method that could handle the function calling if
necessary. That would allow you to keep the underlying function simple and
simulations.
(b) Some implementations have an ADVICE feature that allows you to add a
wrapper around a regular function. This would not, of course, be portable
unless you wrote your own version of it.
Robert Munyer
2019-12-16 23:54:56 UTC
Post by Mirko Vukovic
Hello,
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or
or
(add-two (lambda() 2) (lambda () 3))
- Is there a name for this concept
- Is there a better way of doing it
I don't think that exact construct is popular enough to have a name.
are related to your construct and may give you some good ideas.
--
-- Robert Munyer code below generates e-mail address

(format nil "~(~{~a~^ ~}~)" (reverse `(com dot munyer at ,(* 175811 53922))))
Jeff Barnett
2019-12-17 00:08:54 UTC
Post by Robert Munyer
Post by Mirko Vukovic
Hello,
As an example, consider a function that adds two numbers.
I want to call it by passing either two numbers, or functions
or