The on-wiki version of this newsletter can be found here:
https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2022-01-28
--
Since the start of the project, we have published the function model for
Wikifunctions
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Function_model>, 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
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2020-11-10>, what
are functions good for
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2020-10-07>, labels
and documentation
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2020-10-22>,
what are generic types
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2021-01-21>,
and why we allow for multiple implementations per function
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2021-06-17>.
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 <https://en.wikipedia.org/wiki/Subroutine>, mathematics
<https://en.wikipedia.org/wiki/Function_(mathematics)>, linguistics
<https://en.wikipedia.org/wiki/Functional_linguistics>, biology
<https://en.wikipedia.org/wiki/Function_(biology)>, engineering
<https://en.wikipedia.org/wiki/Function_(engineering)>, and sociology
<https://en.wikipedia.org/wiki/Structural_functionalism>. 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
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2020-10-07>:
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
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2020-10-22>.
*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 above, 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
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2021-06-17> for
a function.
We hope that helps with understanding functions, testers, and
implementations better.
The on-line version of this newsletter can be found here:
https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2022-01-21
--
This week we are happy to welcome Luca Martinelli on the team!
Here is Luca's introduction in his own words:
Hello, I’m Luca (aka Sannita). I've been a Wikipedian since Jan 15, 2006
(yep, it’s been 16 years!), and also a Wikidatan since the inception of the
project back in 2012. I also served as a Board member for Wikimedia Italy
from 2014 to 2017.
I was born in Benevento, Italy, but moved to Rome when I was almost 17. I
have lived there since (except for a year abroad in wonderful Ljubljana,
Slovenia), as well as working there as journalist and communications
manager.
From 2014 to 2019, I worked as webmaster and later Wikipedian in Residence
at the Central Institute for the Union Catalog
<https://www.wikidata.org/wiki/Q3803707>, that manages Italy’s National
Library Service (SBN). Finally, I joined Wikimedia Foundation in January
2021 as a contractor, first working on the Universal Code of Conduct, then
as a Community Relations Specialist.
In my free time, I enjoy mostly watching movies or series, reading or
spending time with my friends. I also enjoyed traveling around the world,
attending Wikimania and/or other wiki-related events (and waiting to start
doing it again).
Luca will work together with Quiddity on Community Communications (both
part-time), strengthening our team's capacity here and covering more time
zones. As the launch of Wikifunctions comes closer, it is important that we
have sufficient resources available to support the new community that will
emerge around Wikifunctions. Starting a new project is something the
Wikimedia movement does rarely, and we believe that the initial start of a
community has a long-term effect on how the project and the community will
evolve. You can find Luca on the wikis as User Sannita
<https://meta.wikimedia.org/wiki/User:Sannita_(WMF)>.
We are very excited to have Luca on board, and invite you to give him a
warm welcome!
The on-wiki version of this newsletter can be found here:
https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Updates/2022-01-13
--
Happy New Year, and welcome to the first newsletter of 2022!
When we started work on Wikifunctions, we divided the work into eleven
functional phases
<https://meta.wikimedia.org/wiki/Abstract_Wikipedia/Phases>, named after
the first eleven letters of the Greek alphabet (a naming scheme that has
received notoriety recently). We are currently working on Phase η (eta),
with the goal of entirely re-doing the function model of Wikifunctions by
allowing for generic types and functions.
Following discussions within the team, we are refining the completion
criteria for the current phase, in order to make it more tangible. The
original idea of how to show that we had completed the phase was that we
would do a lot of very deep static validation, so that a function call such
as:
(1) if(head([true]), false, true)
would validate, but
(2) if(head(["text"]), false, true)
would not. To explain: the if function requires a Boolean as the first
argument, but head is currently defined as head(List) → Object. So in a
static analysis, if requires a Boolean but gets an Object. But if we would
say that this is OK, the static analysis for both (1) and (2) would pass -
and (2) shouldn’t pass.
On the other hand, as well as static analysis we have dynamic analysis,
which happens when running the code. In that one, (1) should pass and
return false, whereas (2) should raise a type error during evaluation
(something such as “if expects a Boolean on the first argument, but got a
String”).
We decided to de-scope the deep static analysis for now. This is something
that we can add later, possibly after launching Wikifunctions.org. As you
can see, users would get error messages one way or the other: it is just a
question of when exactly the error is raised. Also, if you are interested
in working on these or similar topics, please reach out. We are always
happy to integrate the work of volunteers and other external partners.
Instead of this static analysis, we decided to focus on the following
capabilities as examples of how the phase will have been completed:
- Being able to implement curry as a composition on the wiki, but
without requiring strict static analysis
- Making it possible to create the following three 'user-defined' types
on the wiki: positive integer, sign, and integer
- Being able to make a generic wrapper type through composition on the
wiki
- Providing a re-designed function editor (though this might be pushed
into the next phase)
Let’s dive deeper into each of these capabilities.
Curry
For this to work, a user must be able to use a function as an argument, and
use that function for a function call in the implementation of curry. What
is curry? Curry <https://en.wikipedia.org/wiki/Currying> is a standard
function in computer science that takes a function f and another object x
and runs the function on the object. The standard form is that the function
f has two arguments, the first one being of the type of x, and the 'output'
is a call to the function f with the second argument preset to the value x.
Example: given a function that appends a string to another string, I could
create a new function “add s to end” by calling curry(append, "s"). We want
to make it possible to define and call such a function.
User-defined typesPositive Integer
The following things will need to be possible:
- Create a new type, Positive Integer, with a single key value of type
String
- Create a function to validate Positive Integer
- If value contains a character which is not a digit (0-9), the
validator raises a user-created error with the non-digit character
- If value contains leading zeros and is not just a single zero, the
validator raises a user-created error
- If value is an empty string, the validator raises a user-created
error
- Connect the validator to the Positive Integer type
- Create the following eight functions:
- Is zero: Positive Integer → Boolean (returns True if the argument
is zero and False otherwise)
- Successor: Positive Integer → Positive Integer (returns the
argument increased by one)
- Provide implementations in JavaScript (BigInt) and Python (bignum
)
- Predecessor: Positive Integer → Positive Integer (returns the
argument decreased by one, raises an error in case the argument is zero)
- Provide implementations in JavaScript (BigInt) and Python (bignum
)
- Positive Integer: String → Positive Integer (constructor)
- Positive Integer as String: Positive Integer → String
(deconstructor)
- Equal Positive Integer: Positive Integer, Positive Integer →
Boolean (whether
the two numbers have the same value)
- Greater Than: Positive Integer, Positive Integer → Boolean (whether
the first number is bigger than the second)
- Lesser Than: Positive Integer, Positive Integer → Boolean (whether
the second number is bigger than the first)
Sign
The following things will be possible, related to the mathematics term
"sign":
- Create a new type, Sign, with a single key identity of type Sign
- Create the three possible values of type Sign, i.e. positive, negative,
and neutral
- Create a function to validate Sign
- If the value is not one of the expected three values, raise an error
- Connect the validator to the Sign type
- Create the following function:
- Equal sign: Sign, Sign → Boolean (returns True if the two arguments
have the same value)
Integer
The following things will be possible:
- Create a new type, Integer, with two keys, absolute value of type Positive
Integer and sign of type Sign
- Create a function to validate Integer
- Ensure that both keys are validated
- If sign is neutral and absolute value is not zero, or *vice versa*,
raise an error
- Connect the validator with the Integer type
- Create the following six functions:
- Integer: Positive Integer, Sign → Integer (constructor)
- Absolute value: Integer → Positive Integer (deconstructor)
- Sign: Integer → Sign (deconstructor)
- Equal Integer: Integer, Integer → Boolean (whether the two numbers
have the same value)
- Greater Than: Integer, Integer → Boolean (whether the first number
is bigger than the second)
- Lesser Than: Integer, Integer → Boolean (whether the second number
is bigger than the first)
Generic wrapper
A generic wrapper is a function that takes a type and returns a newly
constructed type that has a single key with a value of that type. Wrappers
may be useful for example to make sure that we don’t accidentally treat
something as a number that looks a lot like a number. It is also useful as
a test case because it is a rather simple generic type.
- Create a function that creates the generic type, i.e. Wrap Type: Type
→ Type
- Create a generic constructor function, i.e. Wrap: Type T → Function which
is a function that returns a function of signature T → Wrap Type(T)
- Create a generic deconstructor function, i.e. Unwrap: Type T →
Function which
is a function that returns a function of signature Wrap Type(T) → T
- Store a literal of type Wrap Type(Boolean) as an object on the wiki
- Write a generic function that creates a function that works with Wrap
Type(T), e.g. Wrapped Equality: Type T → λ: Wrap(T), Wrap(T) → Boolean
- Note that all these functions should be possible to be written
entirely in user space
- Test by calling something like Unwrap(Boolean)(Wrap(Boolean)(True)) and
get True back
Where to next?
There are still quite a few tasks to be done in this phase, but many of the
pieces are almost in place for it. Once this phase is complete, we will
focus on getting the beta cluster ready for you to play with.
We wish you all the best for the New Year!