72

According to my understanding, partial functions are functions that we get by passing fewer parameters to a function than expected. For example, if this were directly valid in Python:

>>> def add(x,y):
...    return x+y
... 
>>> new_function = add(1)
>>> new_function(2)
3

In the snippet above, new_function is a partial function. However, according to the Haskell Wiki, the definition of partial function is

A partial function is a function that is not defined for all possible arguments of the specified type.

so, my question is: what exactly is meant by "partial function"?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Saurabh kukade
  • 1,608
  • 12
  • 23
  • 42
    You are confusing a *partially applied* function with a *partial* function. – Willem Van Onsem Oct 11 '19 at 10:13
  • 13
    Python's `partial` performs [partial application](https://en.wikipedia.org/wiki/Partial_application), whereas Haskell does that automatically. The wiki entry refers to [*partial functions*](https://en.wikipedia.org/wiki/Partial_function), which is a term from mathematics. – L3viathan Oct 11 '19 at 10:15
  • 10
    Strictly speaking, Haskell doesn't do partial function application. Every function takes one argument, and function application applies a function to a single argument. Currying *simulates* what you would think of as partial application in another language by simulating multiple-argument functions in the first place. Something like `add 3 5` isn't a single function application. This first applies `add` to 3 to get a new function, which is then applied to 5. – chepner Oct 11 '19 at 11:19
  • And in C#, a `partial` method is a forward declaration of an _optionally_ implemented private method elsewhere in the project codebase. – Dai Oct 12 '19 at 02:01
  • 1
    Your example could be made valid: `new_function = functools.partial(add, 1)` – wjandrea Oct 12 '19 at 02:21
  • Your example (and thus the question) is invalid: `Traceback (most recent call last): File "", line 1, in TypeError: add() missing 1 required positional argument: 'y'` – ruohola Oct 14 '19 at 09:27

3 Answers3

94

You are here confusing two concepts. A partially applied function [haskell-wiki] with a partial function [haskell-wiki].

A partially applied function is:

Partial application in Haskell involves passing less than the full number of arguments to a function that takes multiple arguments.

whereas a partial function indeed is a non-total function:

A partial function is a function that is not defined for all possible arguments of the specified type.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 28
    This is a good answer, but it could be improved by adding an example of a partial function to the answer itself. – ApproachingDarknessFish Oct 13 '19 at 06:00
  • 5
    I'm not sure I agree with that exact definition of a partially applied function. Functions in Haskell always only take one argument, never "multiple arguments". I would use the definition "partial application (partially applying functions) in Haskell involves supplying fewer than the full number of arguments needed to obtain a value that cannot be further applied to another argument." (adapted from [here](https://stackoverflow.com/a/47700022/1971805)) – TerryA Oct 14 '19 at 00:50
  • @TerryA This is a bit philosophical, but I would say that Haskell *has* mutli-argument functions and it *implements* that concept by mapping them to a particular pattern of simpler unary functions. `add x y = x + y` is a binary function and it is also a unary function that returns a function; in Haskell those are two equivalent ways of describing the same thing (one is not more correct than the other). "Partial application" is inherently a concept that arises when looking at things from the multi-argument viewpoint; there's no need for a clunky definition in terms of unary functions. – Ben Nov 18 '22 at 01:09
25

A partial function (both in the context of functional programming and mathematics) is exactly what the wiki says: a function not defined for all of its possible arguments. In the context of programming, we usually interpret "not defined" as one of several things, including undefined behaviour, exceptions or non-termination.

An example of a partial function would be integer division, which is not defined if the divisor is 0 (in Haskell it will throw an error).

in above snippet new_function is partial function.

That code would simply cause an error in Python, but if it worked as you intended, it would be a total (meaning not partial) function.

As commentors already pointed out, you're most likely thinking of the fact that it'd be a partially applied function.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
21

The answers explain all, I will just add one example in each language:

def add(x,y):
    return x+y

f = add(1)
print(f(3))

    f = add(1)
TypeError: add() missing 1 required positional argument: 'y'

this is neither a partial function nor a curried function, this is only a function that you didn't gave all its arguments.

A curried function in python should be like this:

partialAdd= lambda x: lambda y: x + y

plusOne = partialAdd(1)
print(plusOne(3))

4

and in haskell:

plus :: Int -> Int -> Int
plus x y = x + y

plusOne = plus 1

plusOne 4

5

A partial function in python:

def first(ls):
    return ls[0]

print(first([2,4,5]))
print(first([]))

output

2

print(first([]))
  File "main.py", line 2, in first
    return ls[0]
IndexError: list index out of range

And in Haskell, as your link showed up:

head [1,2,3]
3

head []
*** Exception: Prelude.head: empty list

So what is a total function?

Well, basically the opposite: this is a function that will work for any input of that type. Here is an example in python:

def addElem(xs, x):
  xs.append(x)
  return xs

and this works even for infinite lists, if you use a little trick:

def infiniList():
    count = 0
    ls = []
    while True:
        yield ls
        count += 1
        ls.append(count)

ls = infiniList()
for i in range(5):
  rs = next(ls)

print(rs, addElem(rs,5))

[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]

And the equivalent in Haskell:

addElem :: a -> [a] -> [a]
addElem x xs = x : xs

addElem 3 (take 10 [1..])
=> [3,1,2,3,4,5,6,7,8,9,10]

Here the functions don't hang forever. The concept is the same: for every list the function will work.

Rainald62
  • 706
  • 12
  • 19
developer_hatch
  • 15,898
  • 3
  • 42
  • 75
  • 1
    Its worth mentioning that python has support for [partial functions](https://docs.python.org/3/library/functools.html?highlight=functools#functools.partial) in the standard library. – Lord Elrond Jan 09 '20 at 11:04