How do we represent functions?

Since the start of the project, we have published the function model for Wikifunctions, but the description is rather technical and uninviting. In these newsletters, we have previously described parts of the function model in a more accessible way: what is an object, what are functions good for, labels and documentation, what are generic types, and why we allow for multiple implementations per function.

Today, let’s dive into a core concern: how do we represent a function?

What is a function? The term “function” is used in various different disciplines, and the English Wikipedia offers articles on functions in computer science, mathematics, linguistics, biology, engineering, and sociology. All of these notions are related and interesting, and for Wikifunctions we use the following, specific definition: a function takes some input and responds with an output. The input affects the output in a predictable way.

What are functions good for? We previously discussed the general motivation for functions: functions answer questions, and answering questions is a crucial part of allowing everyone access to the sum of all knowledge. Functions will be the central type of objects in Wikifunctions . Users of Wikifunctions will be able to “call” or use functions to answer their questions. Contributors to Wikifunctions will be able to define new functions and create the related supporting objects.

How do we represent a function? Here’s the overview of how a function is represented in Wikifunctions and the related objects supporting it (we’ll go into these in detail below): a function has labels, aliases, documentation, a return type and a list of arguments, a list of testers, a list of implementations, and an identity. (We will not discuss identity today, as this is rather technical)

Labels. We define a function by giving it a label, a name. The label depends on the language: a function that adds up two numbers may be called “Addition” in English, “Zbrajanje” in Croatian, “Сложе́ние” in Russian, “যোগ” in Bangla, etc. It might also have aliases that will help people to find the right function: “Add”, “Sum”, or “Plus” might be good aliases in English, “прибавле́ние” might be an alias in Russian, and “+” might be an alias that works in many languages.

Documentation. A function will also often have documentation – some text that describes what the function does and how it does it. This should help people with differentiating the function from similar functions, and for allowing users to understand what the function call will result in and how that answers their questions. We have previously discussed labels, aliases, and documentations.

Return type. The return type tells us what type of object the answer of the function will be. For example: “number”, “boolean” for true/false, “string” for words, etc. This can be helpful in many ways: it can help when browsing the available functions, as we sometimes know that our answer will be a number; it can help when combining functions together, because we know that an argument can only take a specific type, and that we can compose it with a function that returns the required type. The return type can also help us with understanding the function and what exactly it returns. For the two “divide” functions mentioned below, one will return a number, and the other a list of strings. Even if we don’t have any documentation for the function in our language, this will give us strong hints about what it might do.

List of arguments. A function takes some input. The input is given as one or more arguments. Each argument can have a label/name (again, given in each of the languages we support), and the type of object we expect this argument to be. The type is important: say you have a function called “divide”, which takes a string and a number, and divides the given string into equally sized parts. So you call “divide “Wikipedia” into 3 parts” and the function answers with “Wik”, “ipe”, and “dia”. Separately, there is another function, also called “divide”, that takes two numbers, to determine how many times the first goes into the second. These are two very different functions, and the result of “divide(333, 3)” could either be 111 or 3, 3, and 3 — depending on which function you picked, and what types the values had.

The numerical division function also illustrates the need for names for the arguments: in that case, it is important which argument is the number by which we divide and which is the number that is being divided. In other cases, the name of the arguments are less important: for the “divide a string into equal parts” function, the types are sufficient to know which argument does what.

List of testers. Testers will play a crucial role in Wikifunctions, and not only by fulfilling the standard role of tests. Traditionally, a test aims to give the user some confidence that a function indeed returns with the correct answer. When a function passes its tests, it is likely that the function will return the right answer also for other arguments. A well-designed set of tests can explore the “edge cases” of what is expected when you call a function with a surprising input.

But in Wikifunctions, the testers will do so much more! When a contributor creates the function that divides a string into equally long parts, they could immediately give examples of what they intended: they can create a tester that, given that we want to turn “Wikipedia” in three equal parts, we want to get a result that is a list of three strings, “Wik”, “ipe”, and “dia”. No matter what language they speak, every other contributor can see the expected results in a well-defined way, and can help with creating implementations that pass the testers.

The testers act as a means to hammer out agreement between the contributors to Wikifunctions about what a function does exactly, no matter the language they speak. The testers are where edge cases (and how they should be resolved) can be suggested, and agreed or rejected. The testers ultimately constrain what the “correct” implementations of the function are, and thus precisely what the function is intended to do.

This also means that the testers are a form of language-independent documentation, as they provide examples of how the function is called and what results are expected. So even if there is no documentation available in your language, the testers might still allow you to guess what a function does, and use it productively.

List of implementations. An implementation makes it possible to actually run the function on the given arguments, and to produce a result to answer the function call with. In Wikifunctions, implementations can be given in three different ways: either as a built-in, as code in a specific programming language, or as a composition of other functions. We have talked about these before, and we have discussed why we are going the unusual way of allowing for multiple implementations for a function.

We hope that helps with understanding functions, testers, and implementations better.