0

I am trying to write a python extension module where some of the functions are curried, but I am not quite sure how to go about doing so. The main difficulty being that I am not sure how to create and return a PyFunction object and how to then pass it the parse rules for its arguments. Is there a fairly efficient way to do this or is this insanity?

From the python side the desired semantics would be:

# given a function f(x, y)
f(a, b) -> result
f(a)    -> f'
f'(b)   -> result

1 Answer 1

1

Let's have a look at a possible Python implementation first.

def f(x, y=None):
    if y is None:
        return lambda y: f(x, y)
    return 'result'

The only thing which needs to be done in C here is creating the lambda function somehow. Here we have the issue not knowing about the PyCFunction calling the C function itself. So we have to write wrapper around this and creates a new PyCFunction object.

static PyObject* curried (PyObject *old_args, PyObject *new_args);
static PyMethodDef curried_def = {"curried", curried, METH_VARARGS, "curried"};

static PyObject* f (PyObject *self, PyObject *args) {
    PyObject *x = NULL, *y = NULL;
    if(!PyArg_ParseTuple(args, "O|O", &x, &y))
        return NULL;

    // validate x
    if (y == NULL)
        return Py_INCREF(args), PyCFunction_New(&curried_def, args);
    // validate y

    // do something to obtain the result
    return result;
}

static PyObject* curried (PyObject *old_args, PyObject *new_args) {
    Py_ssize_t old_args_count = PyTuple_Size(old_args);
    Py_ssize_t new_args_count = PyTuple_Size(new_args);
    PyObject *all_args = PyTuple_New(old_args_count + new_args_count);
    Py_ssize_t i;
    PyObject *o;
    for (i = 0; i < old_args_count; i++) {
        o = PyTuple_GET_ITEM(old_args, i);
        Py_INCREF(o);
        PyTuple_SET_ITEM(all_args, i, o);
    }
    for (i = 0; i < new_args_count; i++) {
        o = PyTuple_GET_ITEM(new_args, i);
        Py_INCREF(o);
        PyTuple_SET_ITEM(all_args, old_args_count + i, o);
    }
    return f(NULL, all_args);
}

This yields the desired semantics of

f(a, b) -> result
f(a) -> <built-in method curried of tuple object at 0x123456>
f(a)(b) -> result

Here we abuse the PyCFunction type a little and the second parameter passed to PyCFunction_New(&curried_def, args) is supposed to be the self object this function is bound to, hence we'll get a built-in method curried of tuple object. If you need the self parameter of the original function or use keyword arguments, you'd have to extend this hack a little and build a custom object to pass instead of the args. Also it's possible to create a type like of PyCFunction for curried functions. As far as I know, there isn't anything like that yet.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.