1

I am experimenting with learning C and Currying (after learning a bit of Haskell the other day) and I wanted to know if it was possible to do something similar in C.

This is purely "for fun", I've looked at GCC and see that it supports nested functions (non-standard) so was wondering if this approach would work.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

uint32_t (*addCur(uint32_t ad))(uint32_t ad) {
    uint32_t add(uint32_t to) {
        return ad + to;
    }
    return add;
}

int main(int argc, char **argv) {
    uint32_t a = 1, b = 2;

    if(argc > 1)
        a = atoi(argv[1]);
    if(argc > 2)
        b = atoi(argv[2]);

    uint32_t result = addCur(a)(b);
    printf("result: %d\n", result);

    return 0;
}

Which when run gives me the desired effect I am looking for.

./Currying 5 7
result: 12

What I'd like to do is to implement a "multiply and add" method. So in essence have three nested functions, where each function returns the function but leaves the necessary scope to the appropriate variables.

I.E. in JavaScript you can do the following:

let multiplyAndAdd = (x) => (y) => (z) => (x*y) + z;
multiplyAndAdd(3)(4)(5);

Which gives 17.

Calum A
  • 67
  • 7
  • 1
    Returning `add` is undefined behavior because it refers to data on the stack of `addCur`'s invocation. `add` can be used as a "downward-funarg", i.e. it can be passed to other functions that will invoke it before `addCur` completes. – user4815162342 Jun 26 '21 at 18:26
  • 1
    Your code isn't a legal use of the nested function extension, since you call the function `add` after its parent function `addCur` has returned. https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html: "If you try to call the nested function through its address after the containing function exits, all hell breaks loose." – Nate Eldredge Jun 26 '21 at 18:26
  • @user4815162342 Ahh yes, good spot. This is never going to be used in live code. Purely a fun challenge. The code does work. That doesn't mean it's good code. Could you "malloc" the function pointer and therefore leave a memory leak? – Calum A Jun 26 '21 at 18:28
  • @NateEldredge That's funny, I was using that documentation to implement this, but didn't actually read the entire page. – Calum A Jun 26 '21 at 18:36
  • 2
    @CalumA: "The code does work" means nothing at all in a language like C. This code isn't merely "not good", it's *wrong*. Trivial changes in surrounding code or compilation options can and will break it badly. If you use it in production it'll probably be a security exploit. Don't form the habit of letting such code survive, not even for testing. – Nate Eldredge Jun 26 '21 at 18:37
  • 1
    Mallocing the function pointer won't help in any way. The problem in a typical implementation is that the code for `add` implicitly refers to `ad` which is in `addCur`'s stack frame, and after `addCur` returns, that memory location is subject to being overwritten at any time. I suppose you could come up with a version using non-nested functions and a `static` variable, but then you will have reentrancy problems. – Nate Eldredge Jun 26 '21 at 18:42
  • Found https://stackoverflow.com/questions/4393716/is-there-a-a-way-to-achieve-closures-in-c or https://en.wikipedia.org/wiki/Blocks_(C_language_extension) if you're willing to work with an extended version of C – BaseZen Jun 26 '21 at 18:42
  • @NateEldredge Yeah, obviously if it were global to the compilation unit... But that's part of the challenge. I absolutely agree with your comments above, if I ever saw this on a Pull Request I'd reject it. But I never heard of "Currying" until the other day and was just intrigued if a similar setup could be done in C. (Not C++ etc). – Calum A Jun 26 '21 at 18:44
  • 3
    So the answer is, for all intents and purposes, "no". – Nate Eldredge Jun 26 '21 at 18:51
  • If you want to program in a Haskell-like language, you know where to find the best one. And it's not called "C". – n. m. could be an AI Jun 26 '21 at 19:58
  • You couldn't do it in C by itself. However, while I haven't tried it myself, I can imagine some ways to do it with preprocessor macros and some in-line assembly. – Iguananaut Jun 26 '21 at 23:17

1 Answers1

-1

This was the answer I was looking for.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/* Type defining the function returns */
typedef uint32_t (*addCur)(uint32_t ad);
typedef addCur (*multiply)(uint32_t m);

multiply multiplyAndAdd(uint32_t x) {
    addCur doMultiply(uint32_t m) {
        uint32_t mx = m*x;

        uint32_t doAdd(uint32_t c) {
            return mx + c;
        }

        return doAdd;
    }

    return doMultiply;
}

int main(int argc, char **argv) {
    uint32_t a = 4, b = 5, c = 6;

    if(argc > 1)
        a = atoi(argv[1]);
    if(argc > 2)
        b = atoi(argv[2]);
    if(argc > 3)
        c = atoi(argv[3]);

    uint32_t result = multiplyAndAdd(a)(b)(c);
    printf("result: %d\n", result);

    return 0;
}

It helped to type define the return type of each method. This enables the methods to be more legible. In turn, this allowed me to chain the returns of each method easier.

As mentioned in the comments, this is potentially dangerous code as there is no guarantee that the stack allocated functions will even exist when the function returns. But I was merely mused at the prospect of whether it could be done in theory without significant amount of code.

Calum A
  • 67
  • 7
  • I don't agree with posting this as an answer. It is dangerously wrong code (no "potentially" about it) and I don't like the precedent of such code appearing in SO answers which others may refer to. – Nate Eldredge Jun 28 '21 at 18:39
  • @NateEldredge Yeah fair enough bud. As I have already said, this was merely a "fun challenge" as a brain teaser. I learn by experimenting and if nothing else I've at least learnt something, I.E. don't do it. – Calum A Jun 28 '21 at 18:47
  • Another problem on a deeper level is that there is only one stored copy of the number to be added or multiplied. Try using this "curry" function to create two curried functions, one of which multiplies by 4 and the other which multiplies by 5, and try to store them both and call them alternately. It fundamentally can't work; at best they will both multiply by 5. Using non-nested functions and a `static` variable fixes the safety problem but can't fix this. – Nate Eldredge Jun 28 '21 at 20:21
  • This is the basic problem with using a raw function pointer as your "curried function" object; they can't incorporate extra data, and so there's no way for two different pointers to the same function to have different effects. You'll have to use an object that can, e.g. `struct curriedFunction { int arg; int (*func)(int, int); };`, plus one helper function to generate a `curriedFunction` and another to call it with a given second argument. – Nate Eldredge Jun 28 '21 at 20:24