17
\$\begingroup\$

I don't like curry. Help me reverse the effects of this evil question - Make me some curry - by uncurrying functions.

Task

  • Given a blackbox curried function, output its uncurried equivalent.
  • The curried function will take a single argument and output either another curried function or a value of another type.
  • The uncurried function you create will take the same number of arguments as the curried function, but all at once.

Rules

  • In languages without first-class functions, you can use an object with a method in place of a curried function - but please specify this.
  • The curried function may also output a function pointer instead.
  • If your language does not allow function pointers or makes passing around functions hard to work with, but allows evaluating strings to make functions (e.g. APL), you can use strings to represent both curried functions and your uncurried output function.
  • You can also take the number of arguments of the functions as an input to help you determine the depth of currying.
  • You may choose to return a function that takes a tuple, list, or other single value containing all the arguments.
  • You may constrain the arguments and return type to a data type of your choosing, but that type should have more than 1 possible value.
  • If f is the inputted curried function and g is your uncurried function, f(a1)(a2)...(aN) must always equal g(a1, a2, ..., aN).
  • The order of arguments in the uncurried function should be the same as in the original curried function.

For example, given the function a -> b -> c -> a + b + c, you can return a function that looks like (a, b, c) -> a + b + c or arr -> arr[0] + arr[1] + arr[2] -> ... but not (c, b, a) -> ... (wrong order). If you also take the number of arguments of the function as input, then (a, b) -> c -> ... would be an acceptable output given the input 2 (2 arguments are curried).

Bounty

Since Rust is the language of the month, I will be offering a 50 rep bounty on Rust answer to this question if you haven't used Rust before.

\$\endgroup\$
  • 1
    \$\begingroup\$ Is there a limit to the number of arguments we must support? Haskell has a hard limit on tuple size (23 I think?). \$\endgroup\$ – Wheat Wizard Dec 28 '20 at 2:32
  • 1
    \$\begingroup\$ By "Given a blackbox curried function, output its uncurried equivalent": The uncurry function receives two arguments, a function and its arguments, currying. \$\endgroup\$ – tsh Dec 28 '20 at 3:06
  • 2
    \$\begingroup\$ What should happen in languages that allow polymorphic returns where the polymorphism may cover function types? For example, in Haskell, id :: a -> a can be also be called at the type id :: (a -> b) -> a -> b. Does id have one argument or two (or three or four or...)? \$\endgroup\$ – Daniel Wagner Dec 28 '20 at 4:56
  • 3
    \$\begingroup\$ @vrintle that's not a curried proc... this is... (what i mean is, the builtin .curry is not what the question means by a curried proc.) \$\endgroup\$ – ASCII-only Dec 28 '20 at 10:49
  • 2
    \$\begingroup\$ @user Would it? Or would it be the unchanged a -> a? Or would it be ((a -> b -> c), a, b) -> c? Or ((a -> b -> c -> d), a, b, c) -> d, or...? But okay, if I can expect the caller to specify the number of arguments, then the goal is more clear. \$\endgroup\$ – Daniel Wagner Dec 28 '20 at 16:33

16 Answers 16

9
\$\begingroup\$

Python 3, 48 bytes

f=lambda g:lambda x,*a:f(g(x))(*a)if a else g(x)

Try it online!

\$\endgroup\$
  • \$\begingroup\$ 47 bytes? \$\endgroup\$ – user Dec 27 '20 at 21:32
  • 3
    \$\begingroup\$ @user no, because a and f(g(x))(*a) could evaluate to something falsey (like 0 or []) then or would cause an incorrect evaluation of g(x) returning a function (e.g. TIO). \$\endgroup\$ – Jonathan Allan Dec 28 '20 at 2:53
  • \$\begingroup\$ Ah, that’s too bad. \$\endgroup\$ – user Dec 28 '20 at 3:33
8
\$\begingroup\$

JavaScript (Node.js), 34 bytes

g=>(...a)=>a.reduce((h,x)=>h(x),g)

Try it online!

-1 byte, thanks to ASCII-only.


JavaScript (Node.js), 31 bytes

g=>(...a)=>(a.map(x=>g=g(x)),g)

Try it online!

This 31 byte version, the returned function is not reusable. I'm not sure if this is allowed in this question...

\$\endgroup\$
  • \$\begingroup\$ 34 \$\endgroup\$ – ASCII-only Dec 28 '20 at 8:20
  • \$\begingroup\$ @ASCII-only Ahh, I wrote the one use reduce first. And find out using map is shorter. Later I noticed that the return value is not reusable. And I forgot to use reduce this time.... \$\endgroup\$ – tsh Dec 28 '20 at 8:28
8
\$\begingroup\$

Haskell, 129 127 bytes, thanks to Wheat Wizard

{-#Language GADTs#-}
data N c u o where Z::N o()o;S::N c u o->N(i->c)(i,u)o
(!)::N c u o->c->u->o
(Z!o)_=o
(S n!g)(i,u)=n!g i$u

Try it online! An unobfuscated version looks like this:

{-# Language GADTs #-}

data Natural curried uncurriedArguments output where
    Zero :: Natural output () output
    Successor ::
        Natural curried uncurriedArguments output ->
        Natural (input -> curried) (input, uncurriedArguments) output

deepUncurry ::
    Natural curried uncurriedArguments output ->
    curried ->
    uncurriedArguments -> output
deepUncurry Zero output () = output
deepUncurry (Successor n) g (input, uncurriedArguments)
    = deepUncurry n (g input) uncurriedArguments

The caller must specify the number of arguments they want uncurried as a natural number, where Z represents 0 and S adds one to a number. For example, to uncurry two arguments, you would use S (S Z):

> :t S (S Z) ! (+)
S (S Z) ! (+) :: Num o => (o, (o, ())) -> o
\$\endgroup\$
5
\$\begingroup\$

R, 68 67 71 bytes

Edit: -1 byte thanks to Giuseppe, but then +4 bytes to fix a bug spotted by ASCII-only

function(f)function(...,g=f)el(rev(sapply(c(...),function(a)g<<-g(a))))

Try it online!

This is a horrible bit of R code, but my attempts using for or while have so far failed.

Creates a function that uses sapply to loop over all its arguments, updating the passed function f in each iteration using the <<- assignment operator to assign in the 'parent' scope. This generates a list of all the intermediate values (all except the last one are partially-uncurried versions of f), so we reverse it and return the new first element (so, effectively the last element), which is the result of the fully uncurried function call.

uncurry=                        # define 'uncurry' as
function(f)                     # a function taking argument f, a curried function,
    function(...){              # that returns an anonymous function  
                                # with an undefined number of arguments, denoted as '...'.
        x=                      # define x as 
            sapply(             # the results of applying a function (see below) to each element of
                c(...),         # the concatenation of all the elements of '...' (so: the arguments)
                function(a)     # this is the function to apply, taking argument 'a' as each element:
                    f<<-f(a))   # the value of f(a)
                                # while updating 'f' to this value, in the parent scope
                                # (so the next iteration will use this new version of 'f')
        rev(x)[[1]]             # now, output the last element of x
                                # (which is the value final, uncurried output;
                                # the earlier elements of x are all the partially-uncurried 
                                # versions of f)
    }

Note that since we use c() to concatenate the argument list ... into a vector (rather than into a list), the arguments are constrained to be all of the same type (as allowed by the challenge rules). For a more-general version that can accept argments of different types, exchange c for list (at the cost of an extra 3 bytes).


R, 65 61 bytes

Edit: -5 bytes thanks to Giuseppe

g=function(f)function(a,...)`if`(nargs()>1,g(f(a))(...),f(a))

Try it online!

Alternative recursive version: shorter, easier to understand, and without horrible scoping hacks. Heavily based on comment by ASCII-only, and made nicer (as well as even shorter) by Giuseppe.

\$\endgroup\$
  • \$\begingroup\$ el(x) instead of x[[1]]? But otherwise a solid answer. \$\endgroup\$ – Giuseppe Dec 28 '20 at 1:07
  • \$\begingroup\$ this appears to be single-use only, so it appears to be an invalid answer \$\endgroup\$ – ASCII-only Dec 28 '20 at 8:32
  • \$\begingroup\$ fixed \$\endgroup\$ – ASCII-only Dec 28 '20 at 8:37
  • \$\begingroup\$ @ASCII-only - Can you explain why you think the original function is 'single-use' only? I'm able to use it over-and-over to 'uncurry' multiple curried functions... \$\endgroup\$ – Dominic van Essen Dec 28 '20 at 8:44
  • \$\begingroup\$ 60 \$\endgroup\$ – ASCII-only Dec 28 '20 at 8:44
4
\$\begingroup\$

JavaScript (Node.js), 42 bytes

f=g=>(x,...a)=>a.length?f(g(x))(...a):g(x)

Try it online! Resulting function assumes that you give it the correct number of arguments.

\$\endgroup\$
4
\$\begingroup\$

Wolfram Language (Mathematica), 19 bytes

Fold[#@#2&]~Curry~2

Try it online!

Returns a function of a list of arguments, i.e. u[f][{a,b,...}]=f[a][b]....

Ironically, a short uncurrying involves Curry. Then again, functions curried with Mathematica's Curry built-in can accept arguments in uncurried form - per the docs, "the structure of brackets is not relevant, only the number of arguments."


To accept a sequence of arguments, i.e. u[f][a,b,...]=f[a][b]..., 20 bytes:

f#@#2&~Fold~f?##&

Try it online!

\$\endgroup\$
4
\$\begingroup\$

R, 60 58 51 bytes

-2 bytes thanks to Dominic van Essen. Nicer form thanks to Giuseppe

function(f)function(a)Reduce(function(f,a)f(a),a,f)

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Note that this can be 58 bytes as a anonymous function (without the "u="), since you don't need to assign a name if you never need to use the name. \$\endgroup\$ – Dominic van Essen Dec 28 '20 at 9:50
  • \$\begingroup\$ oh... right, i had the name from the previous recursive version lmao \$\endgroup\$ – ASCII-only Dec 28 '20 at 10:40
  • \$\begingroup\$ Very nice. When I saw @DominicvanEssen 's answer I was trying to think of a Reduce form but couldn't get it to work. You can also use Reduce(function(f,a)f(a),c(...),f) for the same byte count. \$\endgroup\$ – Giuseppe Dec 28 '20 at 15:31
3
\$\begingroup\$

Underload, 11 bytes

((^)~^~a~*)

Try it online!

Function submission (because Underload full programs can't take input).

The TIO link shows a curried function a(*a(*)*)* that concatenates its three arguments in reverse order (calling it three times by using the (argument)~^ function call syntax that calls a function with one literal argument). Then it uses the program above, calling it with arguments 3 ((::**)) and the same curried function, producing the output (a(*a(*)*)*)^^^. The final line runs this input on the same three literal arguments ((arg3)~(arg2)~(arg1)~^), demonstrating that it produces the same result.

Explanation

((^)~^~a~*)
(         )   define function literal:
    ~^          call {argument 1} with argument
 (^)              lambda f, x: f(x)
                {replacing argument 1 with the return value}
      ~ ~       replace argument 2 with
       a          uneval(argument 2)
         *      compose argument 1 and argument 2
                  {and return the function newly created}

The basic idea here is to exploit the fact that Underload is generally very polymorphic with respect to how many arguments a function takes. In particular, we can take a function which takes two arguments and produces one return value, and call it in a loop in order to create a function that takes more than two arguments; each iteration will steal an additional argument from the argument stack.

Numbers in Underload are basically curried loops with a specific number of iterations; you can think of the number 3 as having an effect similar to lambda f: lambda x: f(f(f(x))) (except with the number of arguments being more flexible than this). So by executing the first argument on lambda f, x: f(x), we effectively end up creating a loop that uncurries a function via simply just calling it repeatedly, consuming an extra argument each time.

This could have been just 8 bytes if not for an off-by-one error: if a function takes 1 argument, it's the same curried and uncurried; if it takes 2 arguments, we have to call it one more time; if it takes 3 arguments, we have to call it twice more, etc.. Arithmetic (other than multiplication and exponentiation) is very verbose in Underload, so instead of fixing the off-by-one error with subtraction, we uneval the function we're uncurrying. The uneval'ed function will consume no arguments and output the original function, so this neatly cancels out one level of uncurrying and thus fixes the off-by-one error.

\$\endgroup\$
  • \$\begingroup\$ Did you accidentally mark this community wiki? \$\endgroup\$ – user Dec 28 '20 at 12:28
  • 5
    \$\begingroup\$ No. I intentionally marked it as community wiki. I think Stack Exchange's reputation system has harmful effects (and it has, among other things, driven me away from the site before now), so keeping my reputation permanently locked at 11 is a way to help avoid that. (I used to delete my account after every answer, but community-wiki'ing every answer is a more convenient way to accomplish that.) Also, I generally think answers shouldn't have ownership and am happy for other people to edit/improve mine, and a community wiki mark helps to express that, too. \$\endgroup\$ – ais523 Dec 28 '20 at 12:33
3
\$\begingroup\$

Ruby, 27 25 24 bytes

-3 bytes thanks to vrintle

->f{->a{a.inject f,:[]}}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ 25 bytes, if you'll place u= in header, making the remaining code a lambda. \$\endgroup\$ – vrintle Dec 28 '20 at 12:10
  • \$\begingroup\$ ah yes, always forget to do that when i convert from recursive xd \$\endgroup\$ – ASCII-only Dec 28 '20 at 12:11
  • 1
    \$\begingroup\$ -1 byte as taking an array of args is allowed ;-) \$\endgroup\$ – vrintle Dec 28 '20 at 12:17
  • \$\begingroup\$ @vrintle I don't think that's specified in the question \$\endgroup\$ – ASCII-only Dec 28 '20 at 12:20
  • \$\begingroup\$ It's just after the rules points. \$\endgroup\$ – vrintle Dec 28 '20 at 13:29
3
\$\begingroup\$

Common Lisp, 54 48 bytes

(lambda(f)(lambda(a)(reduce'funcall(cons f a))))

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Attache, 11 bytes

Fold&:`!##V

Try it online!

Anonymous function that takes input like f[curried, a1, a2, ..., aN].

Explanation

With some more whitespace:

(Fold&:`!) ## V

V is a function that converts its arguments into a list. V[1, 2, 3] yields [1, 2, 3]. Fold is given the execute operation ! as a function (`!) for its first argument, via f&:n. ## composes these two functions.

\$\endgroup\$
2
\$\begingroup\$

Java (JDK), 81 bytes

(-9 bytes thanks to user. -2 bytes after removing curly braces from for loop.)

f->a->{var g=f;for(int i:a)g=((java.util.function.Function)g).apply(i);return g;}

Try it online!

Accepts an arbitrary depth Function<Integer, ?> where ? is either Integer or Function<Integer, ?>, and returns a Function<int[], Object>.

I can't really find rules on Java submissions. I tried to mostly follow the format for what counts as the header and what the code body has to look like from an answer on the inspiration for this question. Let me know if I got anything wrong

\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to the site, and nice first answer! Be sure to check out our Tips for golfing in Java page \$\endgroup\$ – caird coinheringaahing Dec 30 '20 at 0:14
  • 1
    \$\begingroup\$ You can just do this and directly use the Markdown TIO generates. There's no need for a separate header part, and in this case, you don't need an import. \$\endgroup\$ – user Dec 30 '20 at 0:18
2
\$\begingroup\$

R, 56 bytes

g=function(f)function(a,...)`if`(nargs(),g(f(a))(...),f)

Try it online!

Credit to Dominic van Essen and ASCII-only for a decent part of this, so please upvote their submissions as well.

The ... aka dots is a special R construct for functions with arbitrary number of inputs.

\$\endgroup\$
  • \$\begingroup\$ >:D I cheated (rule 5) so mine is shorter now \$\endgroup\$ – ASCII-only Jan 1 at 6:21
1
\$\begingroup\$

PHP, 84 bytes

function u($f){return function($a,...$b)use($f){return$b?u($f($a))(...$b):$f($a);};}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Perl 5, 54 bytes

sub f($n,$g){--$n?sub($p,@p){&{f($n,&$g($p))}(@p)}:$g}

Try it online!

Parameter $n is the number of arguments to uncurry, $g is the input function which has to be uncurried.

\$\endgroup\$
1
\$\begingroup\$

C++ (gcc), 189 bytes

#define T template
T<class F>struct U{F f;auto operator()(){return f;}T<class A,class...B>auto operator()(A a,B...b){return U<decltype(f(a))>{f(a)}(b...);}};T<class F>U<F>u(F f){return{f};}

Try it online!

Function u takes in a curried callable object, and returns another callable object which is uncurried.

(I don't see any allowance for a restriction on the return type of the curried function, just on the argument types. If it were allowed, we could save two bytes by replacing both instances of auto with int.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.