This course has already ended.

The latest instance of the course can be found at: O1: 2024

Luet oppimateriaalin englanninkielistä versiota. Mainitsit kuitenkin taustakyselyssä osaavasi suomea. Siksi suosittelemme, että käytät suomenkielistä versiota, joka on testatumpi ja hieman laajempi ja muutenkin mukava.

Suomenkielinen materiaali kyllä esittelee englanninkielisetkin termit.

Kieli vaihtuu A+:n sivujen yläreunan painikkeesta. Tai tästä: Vaihda suomeksi.


Chapter 6.2: Anonymous Functions

About This Page

Questions Answered: What would make higher-order functions easier to work with? How can I define a function “on the fly” as I use it?

Topics: Function literals and anonymous functions. Scala’s shorthand notations for function literals.

What Will I Do? Read and work on small assignments.

Rough Estimate of Workload:? Two hours or so.

Points Available: A35 + B5 + C5.

Related Modules: HigherOrder. We’ll touch on AuctionHouse1, too.

../_images/person09.png

Introduction: A Few Words about Literals

In this chapter, you won’t learn to create new sorts of programs. What you will learn is new ways of writing functions. These new notations aren’t a theoretical exercise but will help you program more efficiently.

But let’s not go there just yet. Let’s start with numbers.

Already in Week 1, you learned to store numbers in variables. You can use a variable name to refer to a number, which is convenient especially when you need to refer to the same number in multiple places within your program.

On the other hand, we don’t always use variable names to refer to numbers. If we have a var named index and wish to increment its value by one within a loop, we typically won’t write this:

val incrementSize = 1
index += incrementSize

Instead, we’ll probably use the integer literal 1 simply to write index += 1. As you know, a literal is an expression that expresses a specific value “directly” or “literally” as written in code. There are many reasons to use a literal in index += 1:

  • A human reader can easily make sense of “incrementing by one” as a single whole.

  • The purpose of 1 in index += 1 is readily apparent from the command, so introducing an additional variable and its name incrementSize doesn’t really make the program any more readable.

  • incrementSize is (presumably) a single-shot variable that isn’t used anywhere else in the program.

  • If we don’t have incrementSize, readers of our program code don’t have to consider whether the variable might have some broader meaning in the program apart from its use in index += incrementSize.

  • Given all of the above, index += 1 is a shorter way to express the same thing without being harder to read. (In fact, it may be easier.)

Enough about numbers. How is this relevant to functions?

Since Week 1, you have used def to define functions that have names. You have used the functions’ names to refer to particular operations much like you have used variables’ names to refer to particular numbers. Function names are especially convenient when you want to call the same function multiple times, perhaps from different parts of the program.

In this chapter, you’ll see that you can also define anonymous, “single-shot” functions.

For that, we need function literals.

Function Literals

In Chapter 6.1 we discussed the following example function, which calls its parameter function twice.

def twice(operation: Int => Int, target: Int) = operation(operation(target))

We used this function to twice add one to a number, and to twice double a number. So we defined and named these two mini-functions:

def next(number: Int) = number + 1

def doubled(original: Int) = 2 * original

Here’s how we used twice in Chapter 6.1:

twice(next, 1000)res0: Int = 1002
twice(doubled, 1000)res1: Int = 4000

There is an easier way to accomplish the same thing. Let’s suppose we’ve defined twice as above. However, we’ll now discard next and doubled; we won’t need them if we define functions “on the fly” as we pass them to twice.

twice(number => number + 1, 1000)res2: Int = 1002

We use a function literal (funktioliteraali) that defines an anonymous function (nimetön funktio). You can read this literal as “an anonymous function that takes in a parameter number and returns the value number + 1”. This anonymous function is passed to twice as its first parameter and consequently invoked two times.

A rightward arrow indicates that this is a function literal. The parameters of the anonymous function are listed on the left (here, we have only one) and body of the function is written on the right (here, it’s a simple sum).

We can similarly use a function literal to double a number twice:

twice(n => 2 * n, 1000)res3: Int = 4000

The function literal defines an anonymous function that returns a value twice as big as its input.

We could have written original or some other name in place of n, here. Where it doesn’t cause confusion, it’s common to use simple names for the parameters of function literals in order to keep the literal short and crisp.

Writing a function literal

The arrow: function literals contain the same sort of arrow that you already know from function types (e.g., the Int => Int in the definition of twice).

Parameter types in a literal: you can often omit the parameter types of a function literal because Scala can work out the types from the context. For instance, in our example above, we didn’t have to explicitly tell the computer the type of number in number => number + 1. Given that we passed that function literal to twice, which expects a function of type Int => Int, it can be inferred that number is an Int.

As said, the computer can very often infer parameter types automatically. When that isn’t the case, you can explicitly mark down the types just like you’ve done with named functions: use round brackets and colons. For example, you can write the literal number => number + 1 in full as (number: Int) => number + 1.

Brackets: when a function literal takes only a single parameter whose type can be inferred, you can omit the round brackets that usually surround parameter lists. Which is what we did in our examples above. If you have multiple parameters, you must write the brackets. (See below for examples.) It’s never wrong to write the brackets.

Anonymous functions as values

Let’s use the REPL to examine how function literals turn into anonymous function objects.

Here’s an increment-by-one literal:

(number: Int) => number + 1res4: Int => Int = <function1>

The REPL confirms: we have a value of type Int => Int.

Here, we actually needed an explicit type annotation for number, because there is no context to indicate its type. (For all Scala knows, we might have intended for number to be a Double or a String, for instance.)

The literal below defines a function that takes two parameters, divides one by the other, and returns a rounded result. It has the type (Double, Double) => Int.

(x: Double, y: Double) => (x / y).round.toIntres5: (Double, Double) => Int = <function2>

References to anonymous functions

A function defined by a function literal is an object. You can use it like you use other objects.

The function itself has no name. However, we can make a variable refer to an anonymous function:

val myFunc = (x: Double, y: Double) => (x / y).round.toIntmyFunc: (Double, Double) => Int = <function2>

Now we can use the variable’s name to call the function:

myFunc(10, 4)res6: Int = 3

It’s also perfectly possible to have another variable refer to the same function:

val roundedDivision = myFuncroundedDivision: (Double, Double) => Int = <function2>

Now this works too:

roundedDivision(100, 8)res7: Int = 13

Functions as objects

In Chapter 5.3, you saw that if a Scala object has a method named apply, it serves as a sort of “default method” for that object. For instance, the expression myObject(params) is simply a shorthand notation for the method call myObject.apply(params). In effect, you can use an object with an apply method “as if it was a function”.

All of Scala’s function object have an apply method, and the following command means precisely the same as the one above.

roundedDivision.apply(100, 8)res8: Int = 13

From an object-oriented point of view, to call a function is to call its apply method.

Why use function literals and anonymous functions?

The strengths and weaknesses of function literals are similar to those of, say, integer literals. Here are some of the strengths:

  • You don’t need to define names for every last tiny function that you use only once.

  • You can write the function literal precisely where you need the function. For example, you can write a literal within a twice call, so that your function definition is affixed to what you do with the function. This can make your code more readable, especially if the function literal is short.

  • Function literals can generally make your code shorter. Brevity is not an end in itself, but it’s a nice bonus unless it results in cryptic code.

Limitations and weaknesses:

  • Anonymous function have no names. Sometimes, the lack of names makes code harder to read.

  • Writing and reading function literals can be a challenge at first (but it soon gets easier with practice).

  • There are many functions that need a name, such as the public methods on objects. Anonymous functions don’t work for all needs.

  • If we wish to call an anonymous function multiple times, we need a variable (a name) that we can use to refer to the function.

Anonymous functions need to be used with care. One excellent use is to pass short anonymous functions as parameters to other functions, as we did above.

Anonymous functions are very common; using them is a skill that pays off to learn. In O1, we’ll be using anonymous functions extensively.

Terminological note: lambda

You may hear function literals referred to as lambda expressions and anonymous functions referred to as lambda functions. Some programming languages also use “lambda” as a keyword; in the Python language, for instance, the expression lambda number: number + 1 corresponds to Scala’s number => number + 1.

That convention has its own history. This sort of expressions originate in the lambda calculus of the mathematician Alonzo Church, who had a pivotal role in the development of computer science.

As for how the Greek letter lambda ended up naming that branch of mathematics, that happened almost literally by pulling the name from a hat: Church didn’t write number => number + 1 but ŷ.y+1, that is, he used a so-called hat on top of the parameter. However, the typesetters of the 1930s apparently had trouble with that and yanked the hat a bit to the left, producing ^y.y+1 where it looked like Λ, a lambda.

Examples of Function Literals

Many of the examples from Chapter 6.1 can be expressed elegantly with function literals. Let’s rewrite some of them.

Example: tabulate

Here is how we used tabulate to initialize a vector:

Vector.tabulate(10)(doubled)res9: Vector[Int] = Vector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18)

We don’t need doubled for that. We can do the same thing neatly with an anonymous function:

Vector.tabulate(10)(index => 2 * index)

A+ presents the exercise submission form here.

Side note: making space for literals

An effortless way to make your code a little bit easier to read is to put space characters around function literals so that they stand out better from their context.

Vector.tabulate(10)( index => 2 * index )

In this ebook, we’ll frequently do this. You may also wish to adopt this convention.

Examples: areSorted and its parameters

In Chapter 6.1, we wrote this higher-order function:

def areSorted(first: String, second: String, third: String, compare: (String, String) => Int) =
  compare(first, second) <= 0 && compare(second, third) <= 0

We also defined these functions that work as parameters for areSorted:

def compareLengths(string1: String, string2: String) = string1.length - string2.length
def compareIntContents(string1: String, string2: String) = string1.toInt - string2.toInt
def compareChars(string1: String, string2: String) = string1.compareToIgnoreCase(string2)

We used them like this:

areSorted("Java", "Scala", "Haskell", compareLengths)
areSorted("Java", "Scala", "Haskell", compareChars)
areSorted("200", "123", "1000", compareIntContents)

Now that we know about function literals, we can rewrite the three expressions above as:

areSorted("Java", "Scala", "Haskell", (s1, s2) => s1.length - s2.length )
areSorted("Java", "Scala", "Haskell", (s1, s2) => s1.compareToIgnoreCase(s2) )
areSorted("200",  "123",   "1000",    (s1, s2) => s1.toInt - s2.toInt )

Don’t forget the brackets.

A+ presents the exercise submission form here.

Example: findAll and a method call in a literal

In Chapter 6.1, we added a findAll method to class AuctionHouse. It’s given a function that represents a search criterion and finds all items that match that criterion.

class AuctionHouse(val name: String):

  private val items = Buffer[EnglishAuction]()

  def findAll(checkCriterion: EnglishAuction => Boolean) =
    val found = Buffer[EnglishAuction]()
    for currentItem <- this.items do
      if checkCriterion(currentItem) then
        found += currentItem
      end if
    end for
    found.toVector

  // etc.

end AuctionHouse

This is how we used findAll:

def checkIfOpen(candidate: EnglishAuction) = candidate.isOpen

def checkIfHandbag(candidate: EnglishAuction) = candidate.description.toLowerCase.contains("handbag")

def findAll(checkCriterion: EnglishAuction => Boolean) =
  val house = AuctionHouse("ReBay")
  house.addItem(EnglishAuction("A glorious handbag", 100, 14))
  house.addItem(EnglishAuction("Collectible Easter Bunny China Thimble", 1, 10))
  println(house.findAll(checkIfOpen))      // finds both auctions
  println(house.findAll(checkIfHandbag))   // finds only the first auction

How would you implement testFindAll with anonymous functions instead? That is, let’s assume that we don’t have the named functions checkIfOpen and checkIfHandbag. How would you rewrite the two last commands that call findAll?

A+ presents the exercise submission form here.

Example: repeatForEachElement

A+ presents the exercise submission form here.

Example: function literals and image processing

Some more old code from 6.1:

 def blueGradient(x: Int, y: Int) = Color(0, 0, x.toDouble / (size - 1) * Color.Max)blueGradient(x: Int, y: Int): Color
Pic.generate(size, size, blueGradient)res10: Pic = generated pic

We can replace the above with this:

Pic.generate(size, size, (x, y) => Color(0, 0, x.toDouble / (size - 1) * Color.Max) ) res4: Pic = generated pic

A+ presents the exercise submission form here.

How about this Pic-generating example?

def artwork(x: Int, y: Int) =
  if x * x > y * 100  then Red
  else if x + y < 200 then Black
  else if y % 10 < 5  then Blue
  else                     Whiteartwork(x: Int, y: Int): Color
Pic.generate(size, size * 2, artwork)res11: Pic = generated pic

The longer your function literal would be, the less likely it is to improve readability. The following literal, for instance, is fairly convoluted. Splitting it across multiple lines helps somewhat, but we’re getting into territory where it would probably be better to use a named function instead.

Pic.generate(size, size * 2, (x, y) => if x * x > y * 100 then Red else if x + y < 200 then Black else if y % 10 < 5 then Blue else White )res12: Pic = generated pic

A turning point in this chapter

Now you know how to use function literals to define anonymous functions. Many but not all programming languages support anonymous functions in one way or another. Anonymous functions are used especially often in the functional programming paradigm, which we’ll say more about in Chapter 11.2.

The second half of this chapter is rather specific to Scala: you will learn another notation for writing function literals.

Anonymous Parameters

When writing a short function literal, the name of the parameter variable often isn’t too important. No matter which of the following you write, it will be obvious to the reader that you’re defining a function that returns an incremented number.

number => number + 1
x => x + 1
something => something + 1

For that reason, Scala lets you leave out the parameter name altogether and so write function literals very compactly.

Consider again the literal number => number + 1, whose core meaning is that 1 gets added to some given value. We can write a literal that captures just that core meaning. This tiny expression defines exactly the same anonymous function:

_ + 1

The underscore means an anonymous parameter. Since our literal contains only a single underscore, it defines a function that takes only a single parameter. One is added to that parameter’s value; the sum is this anonymous function’s return value.

As you see, this abbreviated notation doesn’t feature a right-pointing arrow at all. The underscore is enough to turn the expression into a function literal.

You can use these compact literals just like the full literals:

twice( _ + 1 , 1000)       // returns 1002
twice( 2 * _ , 1000)       // returns 4000

Earlier in this chapter, we listed some of the pros and cons of function literals compared to named functions. Both the pros and the cons are further emphsized in the compact “underscore literals”. If you use them judiciously, they are elegant and improve readability. At first, they may seem nearly magical, but you’ll quickly get used to them.

Annotating parameter types

You can usually leave out parameter types from abbreviated function literals just like you can usually leave them out from the full notation. We have done so in all the examples above.

You can add type annotations to abbreviated literals, too. For instance, you could write _ + 1 as (_: Int) + 1. However, anonymous parameters with type annotations usually aren’t the nicest to read, and we won’t be doing this in O1.

Multiple anonymous parameters

A function can have multiple anonymous parameters: the first underscore in the function literal refers to the first parameter, the second underscore to the second parameter, and so forth.

These two commands, for example, are equivalent:

Vector.tabulate(3, 5)( (row, column) => row + column )
Vector.tabulate(3, 5)( _ + _ )

You can similarly write these familiar function literals:

areSorted("Java", "Scala", "Haskell", (s1, s2) => s1.length - s2.length )
areSorted("Java", "Scala", "Haskell", (s1, s2) => s1.compareToIgnoreCase(s2) )
areSorted("200",  "123",   "1000",    (s1, s2) => s1.toInt - s2.toInt )

in a shorter form:

areSorted("Java", "Scala", "Haskell", _.length - _.length )
areSorted("Java", "Scala", "Haskell", _.compareToIgnoreCase(_) )
areSorted("200",  "123",   "1000",    _.toInt - _.toInt )

A+ presents the exercise submission form here.

A Few Words about Notations

Let’s say we have a collection of elements and we wish to print out each element in it. Here are three instructions to that end, expressed in English:

  • Long form: “Repeat for each element: print x, where x is the element being processed.”

  • Short form: “Repeat for each element: print the element.”

  • Very terse form: “Repeat for each element: print.”

In English, we can choose how we express ourselves based on what suits the context and how we prefer to speak or write.

Scala is pretty flexible for a programming language. Like English, it lets you express this meaning in different ways. Assuming that we’ve defined repeatForEachElement as in Chapter 6.1, and a vector of integers, all of the following do the same thing:

  • Long form: repeatForEachElement(myVector, x => println(x) )

  • Short form: repeatForEachElement(myVector, println(_) )

  • Very terse form: repeatForEachElement(myVector, println)

The latter two are essentially shorthand for the longest version.

Consider another example. Let’s assume myObj refers to some object with an act method that takes an Int parameter.

  • Long form: repeatForEachElement(myVector, element => myObj.act(element) )

  • Short form: repeatForEachElement(myVector, myObj.act(_) )

  • Very terse form: repeatForEachElement(myVector, myObj.act)

Given that all three work equally well, you can make the decision with readability in mind. As with natural language, which one is best depends on the context and on the reader. The very terse form says only what is required; it probably works best when the parameter function is very familiar (like println is). In contrast, the “short form” is more explicit about how each value in turn becomes a parameter: this is a little more apparent from myObj.act(_) than from myObj.act, which looks identical to calling a parameterless method. The “long form” further highlights the parameter variable and lets you name it.

You’ll see examples of all three forms in O1 and in Scala programs written by others. It’s a good idea to know them all even if you prefer one of them yourself.

A rule of thumb

You can always use the long form of function literals — the one with the arrow and the named parameters — until you feel like you’d like a more succinct way to write things.

Restrictions on Anonymous Parameters

A restriction: use parameters just once

Abbreviated function literals have a limitation that is revealed by the following experiment.

Here is a function that uses its parameter to compute a result. The key thing to note here is that the function body features the same parameter more than once.

def compute(number: Double) = number * number + 10

We can express the same computation as an anonymous function:

number => number * number + 10

However, this abbreviated literal fails to do the same thing:

_ * _ + 10

Since each underscore corresponds to a separate anonymous parameter, the above literal does the same as these functions:

(number1, number2) => number1 * number2 + 10
def compute(number1: Double, number2: Double) = number1 * number2 + 10

That is, the literal defines an entirely different function with two parameters.

You can use each anonymous parameter just once within the function literal: each underscore refers to a separate parameter. If you need to refer to a function literal’s parameter multiple times, use a full literal with named parameters.

Here is another example of the same restriction:

def swapGreenAndBlue(original: Color) = Color(original.red, original.blue, original.green)

The parameter variable original appears multiple times in the function body. It’s possible to write a function literal that corresponds to swapGreenAndBlue (and indeed you did so above), but the parameter needs a name.

Another restriction: nested brackets

When you call another function within a function literal, you need to heed the following restriction, which we’ll again explore through examples. These examples make use of the doubled function that we defined earlier.

First, as a point of reference, consider these two expressions that we’ve already discussed. Both increment 1000 twice to 1002:

Longer function literal

Abbreviated literal

Do they work?

twice( x => x + 1 , 1000)

twice( _ + 1 , 1000)

Both work fine.

How about the next example? Here we’d like to do the following twice: add one and double the result. Doing this on 1000 should produce 4006, for instance. So we write a function literal that calls doubled:

Longer function literal

Abbreviated literal

Do they work?

twice( x => doubled(x + 1) , 1000)

twice( doubled(_ + 1) , 1000)

The longer one works.
The shorter one doesn’t.

In this scenario, the compact literal doesn’t mean what we wanted. Instead, the underscore “expands within the inner brackets”, so to speak. The literal doubled(_ + 1) stands for doubled(x => x + 1), which isn’t a valid expression. In situations like this, use the full literal notation.

Below is a third example that does the following twice: first double the number, then add one. Doing this on 1000 should produce 4003.

Longer function literal

Abbreviated literal

Do they work?

twice( x => doubled(x) + 1 , 1000)

twice( doubled(_) + 1 , 1000)

Both work fine.

When there’s nothing at all except the underscore inside the brackets, as in doubled(_), the underscore “expands outside the brackets”. Which is why the compact form works in this case.

These rules may well seem confusing at first. Don’t worry. You can always use the lengthier literal notation.

Practice on Anonymous Parameters

The goal is to call a higher-order method like this:

myPic.transformColors( ??? )

Here’s a function literal that we could use where the question marks are:

pixel => pixel.lighter

May we rewrite this function literal using an anonymous parameter (an underscore)?

The goal is to call a higher-order method like this:

Pic.generate(256, 256, ??? )

Here’s a function literal that we could use where the question marks are:

(x, y) => Color(x, x, y)

May we similarly rewrite this function literal into abbreviated form?

The goal is to call a higher-order method like this:

Vector.tabulate(5, 10)( ??? )

Here’s a function literal that we could use where the question marks are:

(row, column) => column - row * 2

May we rewrite this function literal as _ - _ * 2?

The goal is to call a higher-order method like this:

Vector.tabulate(10)( ??? )

Here’s a function literal that we could use where the question marks are (assuming Random has been imported):

index => Random.nextInt(10) + index

May we rewrite this function literal (as above)?

upperLimit => Random.nextInt(1 + upperLimit)

May we rewrite this function literal (as above)?

How about this expression?

number => 1 + Random.nextInt(number)

The goal is to call a higher-order method like this:

turnElementsIntoResult(vectorOfInts, 0, ??? )

Here’s a function literal that we could use where the question marks are:

(count, nextElem) => count + (if nextElem < 0 then 1 else 0)

May we rewrite this function literal (as above)?

The goal is to call higher-order methods as shown:

val multiplicationTableFrom0 = Vector.tabulate(10, 10)( (row, column) => row * column )
val multiplicationTableFrom1 = Vector.tabulate(10, 10)( (row, column) => (row + 1) * (column + 1) )

We can rewrite the first of those lines like this:

val multiplicationTableFrom0 = Vector.tabulate(10, 10)( _ * _ )

Can we also rewrite the second line like this?

val multiplicationTableFrom1 = Vector.tabulate(10, 10)( (_ + 1) * (_ + 1) )

Summary of Key Points

  • Just as there are literal notations for strings, numbers, and the like, there are literal notations for functions. A function literal defines an anonymous function.

  • If you use them judiciously, anonymous functions can make your code easier to write and easier to read.

  • Anonymous functions are particularly convenient when a short function is needed in a single location only. A typical use case is to call a higher-order function and pass in an anonymous function constructed “on the fly”.

  • Scala has two notations for function literals. The full notation uses a => arrow and names each parameter. The compact notation uses underscores _ to indicate that not only the function, but its parameters too, are anonymous.

    • The full notation always works. The abbreviated notation works for many but not all purposes.

    • It’s fine to always use the full notation in your own code, but you do need to be able to read both notations.

  • Links to the glossary: function literal, anonymous function, anonymous parameter.

Feedback

Please note that this section must be completed individually. Even if you worked on this chapter with a pair, each of you should submit the form separately.

Credits

Thousands of students have given feedback and so contributed to this ebook’s design. Thank you!

The ebook’s chapters, programming assignments, and weekly bulletins have been written in Finnish and translated into English by Juha Sorva.

The appendices (glossary, Scala reference, FAQ, etc.) are by Juha Sorva unless otherwise specified on the page.

The automatic assessment of the assignments has been developed by: (in alphabetical order) Riku Autio, Nikolas Drosdek, Kaisa Ek, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Anna Valldeoriola Cardó, and Aleksi Vartiainen.

The illustrations at the top of each chapter, and the similar drawings elsewhere in the ebook, are the work of Christina Lassheikki.

The animations that detail the execution Scala programs have been designed by Juha Sorva and Teemu Sirkiä. Teemu Sirkiä and Riku Autio did the technical implementation, relying on Teemu’s Jsvee and Kelmu toolkits.

The other diagrams and interactive presentations in the ebook are by Juha Sorva.

The O1Library software has been developed by Aleksi Lukkarinen and Juha Sorva. Several of its key components are built upon Aleksi’s SMCL library.

The pedagogy of using O1Library for simple graphical programming (such as Pic) is inspired by the textbooks How to Design Programs by Flatt, Felleisen, Findler, and Krishnamurthi and Picturing Programs by Stephen Bloch.

The course platform A+ was originally created at Aalto’s LeTech research group as a student project. The open-source project is now shepherded by the Computer Science department’s edu-tech team and hosted by the department’s IT services. Markku Riekkinen is the current lead developer; dozens of Aalto students and others have also contributed.

The A+ Courses plugin, which supports A+ and O1 in IntelliJ IDEA, is another open-source project. It has been designed and implemented by various students in collaboration with O1’s teachers.

For O1’s current teaching staff, please see Chapter 1.1.

Additional credits appear at the ends of some chapters.

a drop of ink
Posting submission...