Talk:Partial function application: Difference between revisions

m
 
(41 intermediate revisions by 8 users not shown)
Line 86:
 
:: Hi Kernigh, the above is ''not'' like Haskel, in fact it doesn't really answer the task as you have made fs a function of only f, then called it with different f. Partial application would be like the Haskel: fs is a function of f ''and s'' and then fsfl is the result of applying only f1 to fs, without mention of any other argument. The above may give a result, but it is ''how'' it gets to that result that is the issue. --[[User:Paddy3118|Paddy3118]] 23:07, 14 April 2011 (UTC)
 
: I believed the description of the Haskell solution: "All functions actually take exactly one argument." I tried to do the same thing for Java: I wrote an ''fs()'' method that takes exactly one argument. The Java syntax is far worse than the Haskell syntax, but the result seems to be the same: ''fs'' takes exactly one parameter ''f'' and returns another function that takes exactly one parameter ''s''. When one says ''fs(arg1).call(arg2)'' in Java, then ''fs'' is a function of two parameters.
 
: One problem is that some program might already have ''fs(arg1, arg2)'', and I want to partially apply it. I might solve this by wrapping the method so I have both ''fs(arg1, arg2)'' and ''fs_curried(arg1).call(arg2)''. It might look like this.
 
: <lang java>static int[] fs(IntegerFunction f, int[] s) {
int[] r = new int[s.length];
for (int i = 0; i < s.length; i++)
r[i] = f.call(s[i]);
return r;
}
 
interface SequenceFunction {
int[] call(int[] arg);
}
 
static SequenceFunction fsCurried(final IntegerFunction f) {
return new SequenceFunction() {
public int[] call(int[] s) {
return fs(f, s);
}
};
}
 
static SequenceFunction fsf1 = fsCurried(f1);</lang>
 
: With the current solution for Java, ''fs(arg1).call(arg2)'' is already a function of two arguments, and there is no reason to also have ''fs(arg1, arg2)''.
 
: Haskell seems to have an analogy for ''fs(arg1, arg2)''. I looked around the Haskell 2010 report, and learned that Haskell has tuples. A function has only one parameter, but that parameter might be a tuple of 2 items. I also found these functions in the [http://www.haskell.org/onlinereport/haskell2010/haskellch9.html Haskell prelude]:
 
: <lang haskell>-- curry converts an uncurried function to a curried function;
-- uncurry converts a curried function to a function on pairs.
curry :: ((a, b) -> c) -> a -> b -> c
curry f x y = f (x, y)
 
uncurry :: (a -> b -> c) -> ((a, b) -> c)
uncurry f p = f (fst p) (snd p)</lang>
 
: It seems that one might define <tt>fs (f, s) = map f s</tt>, a function that takes a tuple and returns a sequence, then use <tt>curry fs f1</tt> as partial application. (There is no Haskell implementation on my computer, so I cannot test this. Perhaps I am wrong.) With the current solution for Haskell, ''fs arg1 arg2'' is already a function of two arguments, and there is no reason to also have ''fs (arg1, arg2)''.
 
: The current Java solution already solves the task: ''fs(arg1).call(arg2)'' is a function of two arguments, and ''fs(arg1)'' is a partial application. --[[User:Kernigh|Kernigh]] 02:15, 15 April 2011 (UTC)
 
==Is Scala correct?==
Line 103 ⟶ 144:
 
I think I will have to mark this as incorrect. --[[User:Paddy3118|Paddy3118]] 23:45, 14 April 2011 (UTC)
 
: I tried Scala. The _ seems to be a "partial application underscore". In ''fs(f1,_:List[Int])'', the _ has no value. So ''fs(f1,_:List[Int])'' returns a function that takes the missing value. The interpreter seems to think that ''fsf1'' is a variable of type ''(List[Int]) => List[Int]''. I have not found the documentation for the "partial application underscore", so I am not sure how it works. I am not the author of the Scala solution. --[[User:Kernigh|Kernigh]] 03:47, 20 April 2011 (UTC)
 
:: Hi Kernigh, if fs were defined as taking 3 arguments and currying would still refer to fs(f1,_) and not fs(f1,_,_) then I would be inclined to accept it as the '_' would stand for "any other arguments" rather than "any one argument" and would retain most of the insensitivity to the number of arguments of the Haskel-type implementations.
 
: <lang scala>scala> def rot(x: Int, y: Int, z: Int) = (y, z, x)
rot: (x: Int,y: Int,z: Int)(Int, Int, Int)
 
scala> rot(2, 3, 7)
res0: (Int, Int, Int) = (3,7,2)
 
scala> def a(y: Int) = rot(_: Int, y, _: Int)
a: (y: Int)(Int, Int) => (Int, Int, Int)
 
scala> a(3)(2, 7)
res1: (Int, Int, Int) = (3,7,2)</lang>
 
: It seems that _ is any one parameter. So, the Scala "partial application underscore" fails the task requirement that "other parameters are not explicitely mentioned". --[[User:Kernigh|Kernigh]] 02:17, 21 April 2011 (UTC)
 
I hope I have fixed this by clearly stating up front that Scala doesn't follow the description and where. This means that there is no doubt as to where the Scala moves away from the task description. The alternative is to delete the example and omit the language from the task. --[[User:Paddy3118|Paddy3118]] 05:35, 21 April 2011 (UTC)
 
: I've never written any Scala code and only seen a few samples, but this does look like partial application to me (quite similar to Perl 6's Whatever-Star). The fact that you have to mention a type signature might just be a limitation of Scala's type inferencer. The key question is: can you use this to pass an arity 3 function to map without using any intermediate definitions or lambda's? &mdash;''[[User:Ruud Koot|Ruud]]'' 07:05, 21 April 2011 (UTC)
 
:: <lang scala>scala> def f(a: Int, x: Int, b: Int) = a * x + b
f: (a: Int,x: Int,b: Int)Int
 
scala> List(1, 2, 3).map(f(10, _, 1))
res4: List[Int] = List(11, 21, 31)</lang>
 
:: Yes, you can pass arity 3 function to map. The _ has no type signature. A _ without a type signature can be an error, so I am not sure why it worked here. (I am new to Scala, and not the author of the Scala solution.) --[[User:Kernigh|Kernigh]] 19:00, 21 April 2011 (UTC)
 
==Is Lisp correct?==
Line 126 ⟶ 197:
: --[[User:Kernigh|Kernigh]] 00:53, 15 April 2011 (UTC)
:: Not only does &rest do the right thing with the arguments, but apply does the right thing with the return value. I think this lisp solution satisfies the concept of partial function evaluation. &mdash;[[User:Sonia|Sonia]] 01:45, 15 April 2011 (UTC)
 
==Is D correct?==
 
The D solution is a not perfect implementation, it's an approximation, but it's a D idiomatic way to solve the problem. If you don't accept an approximation, it becomes hard to implement in D. My suggestion is to accept approximate solutions too.
 
And in the D version fs() is a templated function. It's the way you write generic code in such languages.
 
: HI, some language features are ''ways'' of doing things - like list comprehensions, or case statements. It would be wrong to not use a case statement if one is called for in a task, but in these cases I think it would be acceptable to either:
:# Omit a language if it does not support the feature.
:# Or explicitely state that the feature is missing from the language then go on to explain your languages idiomatic way of supporting the feature.
: Since you state the D solution is idiomatic, then if the comment was changed to something like:
:: ''"D language does not support PFA, but the following solution ..."''
: would be OK by me. What do others think? --[[User:Paddy3118|Paddy3118]] 21:03, 15 April 2011 (UTC)
:: I think it's the call of the primary task author. One way to write a task is to make very specific requirements that a certain technique be used. Languages that can't do it get marked omit, and the result is a clean comparison of that technique in different languages, without the clutter of similar but not-the-same techniques. A different way to write a task is to explain "here's a technique that achieves some goal. Demonstrate the technique in your language, or, if the technique is missing in your language, show the idiomatic way of achieving the same goal." Both are valid ways to write a task, but I really like it when the task description makes it clear one way or the other. For an example, Go doesn't do named or optional parameters. The named parameter task was very specific and I marked it omit. The optional parameter task was worded the second way, even putting in bold "whatever way is most natural to your language." So I enjoyed coding up an alternative way to achieve a similar effect. &mdash;[[User:Sonia|Sonia]] 10:16, 16 April 2011 (UTC)
::: I have added an extra note on not explicitely mentioning other parameters. It's now in twice so I hope the task now matches your first style, although I still thinks it is fine to state that it cannot be done, then give a laguages idiomatic way too. --[[User:Paddy3118|Paddy3118]] 15:52, 16 April 2011 (UTC)
 
Modified the D version. The Task doesn't specify that f has to be a run-time function. In this D implementation f has to be known at compile-time.
 
:Hi, The task description begins ''"Create a function fs( f, s ) that takes a '''function''', f( n ), of one value "''.
:So it is ''required'' that f be a function. Why not just say it is not possible in D if it is not, then explain why before giving the D code? --[[User:Paddy3118|Paddy3118]] 18:26, 16 April 2011 (UTC)
:: Partial application is mainly useful if it can be applied to idiomatically defined functions (in particular functions from the standard library). Perhaps the languages can be subdivided into two categories. One where this is possible and one where this is not? &mdash;''[[User:Ruud Koot|Ruud]]'' 09:34, 17 April 2011 (UTC)
 
== Proposal for new task description ==
 
I would propose to extend the task description here include both currying and partial application:
* Define the linear function ''f'' : (''a'', ''b'', ''x'') ↦ ''ax'' + ''b'' in uncurried form.
* Define the linear function ''g'' : (''a'', ''b'', ''x'') ↦ ''ax'' + ''b'' in curried form.
* Perform the following operations:
*# Curry ''f'', apply ''a'' = 7, ''b'' = 9, apply to a sequence of ''x'''s.
*# Partially apply ''a'' = 7 and ''b'' = 9 to ''f'', and apply to a sequence of ''x'''s.
*# Apply ''a'' = 7 and ''b'' = 9 to ''g'', and apply to a sequence of ''x'''s.
&mdash;''[[User:Ruud Koot|Ruud]]'' 15:32, 15 April 2011 (UTC)
 
:Hi Ruud, I would prefer keeping them separate as it seems it is hard enough getting results for Partial function application on its own. Extending the task would further complicate things. How about a separate Curry task written to emphasize the difference between it and Partial application? --[[User:Paddy3118|Paddy3118]] 20:42, 15 April 2011 (UTC)
 
:: Yes, but currying and partial application are quite closely related and often confused. It may be easier to understand them if they are both used together. Also, if I managed to implement the Ruby example below correctly, currying and partial application seems both to be implemented by Proc#curry. Finally, regarding the complications there seem to be with some of the language: I'm not yet convinced partial application (and currying) can be ''reasonably'' implemented in all languages (as papply and curry are higher-order functions returning a function, I assume you would at least need first-class functions/closures or go through the trouble of emulating those). I'll try to write this task in a few more languages to see if it can be done or not. &mdash;''[[User:Ruud Koot|Ruud]]'' 21:56, 15 April 2011 (UTC)
 
:::Hi again Ruud. I was sure that there would be languages with first-class functions that would not be able to curry/partially-apply. What is new to me is that there may well be languages that are OK with currying, but have problems with partial application as they can't apply a subset of arguments without ''explicitely'' mentioning all arguments in the call to partial. Some manidestly, statically typed languages - i.e. those that require a type signature for everything, would need different and explicit type signatures dependant on how many arguments were being partially applied for example; Haskel works it out for itself.
:::Somehow, I do notice the similarity between currying and PFA, but unlike you, I see it as a strong reason to have two, contrasting tasks. What to do? --[[User:Paddy3118|Paddy3118]] 03:28, 16 April 2011 (UTC)
:::: I think it's somewhat more than a similarity: in the Haskell example we have that <code>papply2</code> = <code>curry</code> and <code>papply3</code> &approx; <code>curry3</code>. In the Ruby partial application and currying are both implemented by <code>Prox#curry</code>. It would be fun to see how a <code>curry</code> function in Python looks that works for functions of any arity like <code>partial</code> does (if this is possible) and how similar their definitions would be. Their currently isn't a task on [[Currying]]. Would it be a problem to create a new task [[Currying and partial application]] or does Rosetta Code strive to avoid duplicated and overlapping entries? &mdash;''[[User:Ruud Koot|Ruud]]'' 09:40, 17 April 2011 (UTC)
 
Here are samples in Haskell and Python:
 
<lang haskell>
module Main where
 
curry3 :: ((a, b, c) -> r) -> a -> b -> c -> r
curry3 f x y z = f (x, y, z)
 
papply2 :: ((a, b) -> r) -> a -> b -> r
papply2 f x = \y -> f (x, y)
 
papply3 :: ((a, b, c) -> r) -> a -> (b, c) -> r
papply3 f x = \(y, z) -> f (x, y, z)
 
f :: (Integer, Integer, Integer) -> Integer
f (a, b, x) = a * x + b
 
g :: Integer -> Integer -> Integer -> Integer -- idiomatic
g a b x = a * x + b
 
g' :: Integer -> (Integer -> (Integer -> Integer))
g' = \a -> \b -> \x -> a * x + b
 
main = let u = curry3 f
v = papply2 (papply3 f 7) 9
w = g 7 9 -- idiomatic
in print [map (u 7 9) [1..5], map v [1..5], map w [1..5]]
</lang>
 
<lang python>
from functools import partial
 
def curry3(f):
return lambda a: lambda b: lambda x: f(a, b, x)
 
def f(a, b, x): # idiomatic
return a * x + b
 
def g(a):
return lambda b: lambda x: a * x + b
 
def main():
u = curry3(f)
v = partial(f, 7, 9)
w = g(7)(9) # non-idiomatic
print [map(u(7)(9), [1,2,3,4,5]), map(v, [1,2,3,4,5]), map(w, [1,2,3,4,5])]
</lang>
 
Possibly-correct Ruby 1.9 implementation:
 
<lang ruby>
def f
proc {|a, b, x| a * x + b}
end
 
def f2(a, b, x)
a * x + b
end
 
def g
proc {|a| proc {|b| proc {|x| a * x + b}}}
end
 
def main
u = f.curry
v = f.curry[7,9]
w = g[7][9]
puts [ (1..5).map {|x| u[7][9][x]} \
, (1..5).map {|x| v[x]} \
, (1..5).map {|x| w[x]} ]
end
 
main
</lang>
 
: With Ruby, you can also curry f2:
 
: <lang ruby>def f2(a, b, x)
a * x + b
end
 
u = method(:f2).to_proc.curry
v = u[7,9]
p [ (1..5).map {|x| u[7][9][x]}, (1..5).map {|x| v[x]} ]
# => "[[16, 23, 30, 37, 44], [16, 23, 30, 37, 44]]"</lang>
 
: I prefer a new task, not a change to this task. --[[User:Kernigh|Kernigh]] 00:25, 16 April 2011 (UTC)
 
=== Current task description is a bit odd ===
Reading over the current task description, I think it is somewhat odd. It asks you to define a function ''f'' taking a single argument, and a higher-order function ''fs'' (essential ''map'') and to partially apply ''f'' to ''fs''. The most common use case of partial application, however, would be, given a function ''f'' of arity at least 2, partially apply arguments until it has remaining arity 1 and then pass this partially applied function to ''fs'' (i.e. ''map''). Even if we do not want to adopt my proposal above, I believe the current task description should still be modified. I would in this case advise ''f'' to be of arity 3, as this makes the task slightly more challenging and interesting for statically-typed languages. &mdash;''[[User:Ruud Koot|Ruud]]'' 10:29, 17 April 2011 (UTC)
 
: My suggested task description would read:
:* Define the linear function ''f''(''a'', ''b'', ''x'') ↦ ''ax'' + ''b''. This function should preferably by defined in an idiomatic form, but if this is not possible other solutions are acceptable.
:* Define the function ''[[map]]''(''f'', ''xs'') which applies its first argument, a function expecting a single argument, to each element of the sequence ''xs''. Preferably use the implementation from the standard library, if available.
:* Perform the following operations: partially apply ''a'' = 7 and ''b'' = 9 to ''f'', and apply this partially applied function to each element of a sequence using ''map''.
: &mdash;''[[User:Ruud Koot|Ruud]]'' 12:46, 20 April 2011 (UTC)
 
:: But how to ensure ''[http://www.haskell.org/haskellwiki/Partial_application partial application]'' is what a Haskel, or other functional programming language programmer would think is partial application? The temptation is for folks to just concentrate on getting the right numerical answer and ignore or miss attempts to constrain ''how'' the result is computed. --[[User:Paddy3118|Paddy3118]] 14:10, 20 April 2011 (UTC)
 
::: This is beginning to sound like a Haskell-specific concept, rather than an algorithmic concept. --[[User:Rdm|Rdm]] 17:52, 20 April 2011 (UTC)
 
:::: Well yes, partial application is a programming language concept, not an algorithm or design pattern. But it can be "done" a number of languages. &mdash;''[[User:Ruud Koot|Ruud]]'' 17:54, 20 April 2011 (UTC)
 
::: Using common sense and discussion? I don't think the task should be overspecified either. For example, specifying that you have to define a function similar to Python's <code>partial</code> would rule out ML's and Haskell's implicit partial application or perhaps a clever, but reasonable, trick involving C's preprocessor or C++'s templates. On the other hand, the current Java implementation might follow the the task to the letter, but certainly violates the spirit as it's not applicable to idiomatically defined functions from the standard library. &mdash;''[[User:Ruud Koot|Ruud]]'' 17:54, 20 April 2011 (UTC)
 
== What is partial application? ==
 
It occurred to me that when one speaks of "partial application" one can refer to several distinct things:
# The ''syntactic sugar'' that allows one to write <code>map (f 7 9) [1..9]</code> or <code>map(f(7,_,9),{1,...,9})</code> instead of <code>map (lambda x: f 7 9 x) [1..9]</code> or <code>def g(x): return f(7,9,x); end def in map(g,(1..9))</code>.
# The ''higher-order function'' <code>functools.partial</code>.
# The ''compilation technique'' that allows function thunks or closures to be "partially applied" instead of only unapplied or fully applied.
The task here should probably be about the first, perhaps also the second. &mdash;''[[User:Ruud Koot|Ruud]]'' 15:35, 21 April 2011 (UTC)
 
== Suggested changes to the task description ==
 
1) There is no need to require functions to be named <code>f1</code>, <code>f2</code>, <code>fsf1</code> or <code>fsf2</code>, and doing so excludes solutions in languages that don't allow digits to be used in identifiers.
 
2) The wording "partially apply <code>f1</code> to <code>fs</code>" should be changed to "partially apply <code>fs</code> to <code>f1</code>" because it's more conventional to speak of functions being applied to arguments than vice versa, and in this case <code>f1</code> is the argument.
 
3) Please be consistent about distinguishing between functions themselves and expressions in which they are applied to an argument. The wording "create a function <code>fs(f,s)</code> that takes a function <code>f(n)</code> ..." should be changed to "create a function <code>fs</code> that takes a function <code>f</code> ...". The expression <math>f(n)</math> refers conventionally not to the whole function <math>f</math> but to the single output associated with the argument <math>n</math> (unless you're an economist, in which case you'll infer an implied universal quantification with respect to the variable <math>n</math>, except when you don't).
 
--[[User:Sluggo|Sluggo]] 18:09, 14 June 2011 (UTC)
Anonymous user