11

I'm not finding via Google any explanation that my brain can grasp. Can someone explain this, and if possible, give an example of each using either pseudocode or C#?

The term 'total' function was introduced to me in the comments section of this question. The comment is:

when you work with total functions, you can compose them with other functions and use them in higher order functions without worrying about error results "not encoded in the type", i.e. exceptions. – Andres F.

I'm intrigued by this comment.

  • 2
    I think this is not a real dupe. This question is about concepts on functional programming (see tag). The referred question is about algorithmic terminology. The wording of the other question is very different and only remotely connected to this one. OP could not have recognized his interest therein. It appears that one answer explains what a total function is, and another refers to total and partial functions without elaborating further. But the fact of having some overlapping elements in the answer is not sufficient IMO for making it a dupe. – Christophe Oct 29 '16 at 22:37
  • This has a community close on it, meaning the question author closed it as a duplicate. Yet there are four reopen votes? Madness! –  Nov 04 '16 at 19:21
  • @Snowman The question author is not the only one who gets to decide :) – Andres F. Nov 04 '16 at 19:50

1 Answers1

20

Here's a reasonable definition from the Haskell wiki:

A total function is a function that is defined for all possible values of its input. That is, it terminates and returns a value.

A partial function is a function that is not defined for all possible input values; in some cases instead of returning a value, it may never return at all (think infinite cycles), throw an exception (which usually states "this is undefined for this input value" or "unexpected input" or whatever) or simply crash the system.

A well-known example of a partial function is the integer division, a divided by b: it's not defined when b is zero!

Another example is head, the Haskell function that takes the first element of a list, which throws an error if the list is empty! Something like this:

head :: [a] -> a  // this is the signature
head (first : rest) = first
head _ = ... // This is an error! What is the first element of the empty list?

Partial functions like head unfortunately exist in Haskell for historical reasons. Note it can be made total like this:

headMaybe :: [a] -> Maybe a
headMaybe (first : rest) = Just first
headMaybe _ = Nothing

Then you can safely call head [] ([] is the empty list) and it will return Nothing instead of throwing.

Note that when looking at the original head, I cannot know just by looking at its definition that it may throw an error in some cases; I need to look at its implementation instead! That's bad. Compare this with headOption: just by looking at its type I know it sometimes returns a Just x and sometimes a Nothing, and I don't have to examine the implementation.

Andres F.
  • 5,139
  • 2
  • 30
  • 41
  • 1
    "Partial functions like head unfortunately exist in Haskell ": Why do you say "unfortunately"? What would be the alternative to head being partial? – Giorgio Oct 28 '16 at 21:49
  • 6
    @Giorgio The alternative would be something like the headMaybe function suggested near the end of the answer. Unlike head, where the user needs to assume/assert the list is not empty, headMaybe returns a Maybe type. When you want to extract the actual value from the maybe type, you need to explicitly state what should be done if it's Nothing. This means you can't use the value at the head of the list without specifying what to do if the list was empty. – Idan Arye Oct 28 '16 at 22:04
  • I think partial functions have their place as a form of assert. In Rust, for example, the Option type(similar to Haskell's Maybe) has the expect method, which means - give me the value, and if it's empty this means the code is buggy and you should crash the program with this error message. – Idan Arye Oct 28 '16 at 22:08
  • @IdanArye Haskell's Maybe has a similar partial function fromJust :: Maybe a -> a. I think they must be used with extreme care, if at all. The problem is that while you may know what you're doing at the exact place where you invoke this, it "taints" the enclosing function as well: now it too may throw an error. And so on and on. Now every caller must either trap the error (which cannot be done in pure code) or accept it has become partial itself. – Andres F. Oct 28 '16 at 22:15
  • That's why they should be treated as a form of assert. You don't ever want to catch and handle assertion errors - if an assertion error triggers it essentially means you have a bug in the code and the program's state is unstable. The only valid reasons to catch such errors is for logging/recording(write the error in a file, mail it to the developer, format it for HTTP response, display failure message in a test framework) or if you want to wrap it with a more meaningful error message. I see no merit in explicitly bubbling these errors up to main, where debuggers have no meaningful context. – Idan Arye Oct 28 '16 at 23:23
  • @IdanArye: If you are not sure whether a list is empty, you can test this with null or destructure the list using pattern matching. While headMaybe is total, you have just moved the problem from testing if the list was empty to testing if the resulting Maybe value is empty. Bottomline: I find it convenient to use a partial function when I am sure that I am calling it on an element of its domain. For the same reason, div has type Integral a => a -> a -> a and not Integral a => a -> a -> Maybe a. – Giorgio Oct 29 '16 at 09:28
  • @Giorgio Like I said to Andres - having to explicitly bubble up the exception using monads all the way to the main function is too much of a hassle, and it's fine to throw an exception to indicate a bug in the code. The only problem is making it explicit - fromJust explicitly conveys that we have a reason to assume we have a Just a and not Nothing. head - not as much. So head myList is saying "give me the head of the list" while fromJust(headMaybe myList) is saying "give me the head of the list, I assume it has one, if no - it's a bug so it's OK to crash the program". – Idan Arye Oct 29 '16 at 15:28
  • @Giorgio That you have to "test with null" is out of band information, so to speak, and that's bad. Merely by looking at head's signature you cannot know this, therefore head isn't "safe". On the other hand, headMaybe is totally safe: there is no way you can make a mistake. Destructuring the list in this case isn't an option, because if you try to destructure [] as x : _ it will fail and you lose type safety. This is why the Haskell community considers head and similar partial functions from the Prelude to be a mistake (that's the consensus by the way, not just my opinion!) – Andres F. Oct 29 '16 at 19:23
  • @Giorgio Another very real problem is that partial functions are harder to use in generic higher-order functions. I'm sure you'll see why :) – Andres F. Oct 29 '16 at 19:25
  • @AndresF.: I totally agree with head not being safe (and, BTW, I do not like partial functions and using exceptions). What I was pointing out is that using headMaybe you neither need to (1) check whether the list is empty, nor (2) catch a possible exception thrown by head or pattern matching. Bu now you have to either (1) check whether the Maybe value contains a value, or (2) catch an exception that is thrown by fromJust or by destructuring the Maybe value. So you have just moved the problem from lists to optional values. A – Giorgio Oct 30 '16 at 09:02
  • @Andres F: "Another very real problem is that partial functions are harder to use in generic higher-order functions. I'm sure you'll see why :)": Again, I was not arguing in favour of partial functions and using exceptions. In general, I am against using exceptions (they should only be used in exceptional cases). I was arguing that the Maybe solution suffers from the same problems as the list solution: you have to destructure the resulting Maybe value, or check that it contains a value, or be prepared to get an exception. – Giorgio Oct 30 '16 at 09:07
  • @Giorgio Maybe I wasn't clear, I wasn't advocating the use of fromJust precisely because it's a partial function! Using Maybe doesn't suffer from the problems we describe, and instead benefits from the full power of the type system. You cannot simply "use the value from Maybe" because the compiler won't let you. Contrast this with explicitly checking for the empty list: the compiler won't enforce this, and you're not taking advantage of the type system! After all, couldn't a Java or C programmer tell you "well, I can just check for the null reference, why do I need option types?" :) – Andres F. Oct 30 '16 at 14:16
  • @Giorgio To make it clearer: with head you don't "have to" check for anything. It's entirely up to you, which makes it dangerous. If you forget: crash! With headMaybe you absolutely must handle all cases (maybe by map'ing over the value, maybe by pattern matching against all possibilities), otherwise the code will not compile. The latter option is closer to the "if it compiles, it works" mantra we all strive for (though of course it's just an ideal :) ) – Andres F. Oct 30 '16 at 14:19
  • @AndresF.: OK, I get your point: head is a standard function whereas fromJust isn't. I totally agree about having the compiler force you to handle all cases. What about div and mod, should they also have type Integral a => a -> a -> Maybe a? – Giorgio Oct 30 '16 at 17:05