Skip to content

Where the Funcs Have No Name

Any self-respecting functional language has a way to create anonymous functions.

In YS there are more than one!

Today we'll talk about nameless functions, why they are useful, and how to create and call them.

What are anonymous functions good for?🔗

A functional language has lots of functions, and a lot of those functions take functions as arguments and/or return functions.

For example, the map function takes a function and a list, and applies the function to each element of the list.

Here we square the numbers 1 through 10.

$ ys -pe 'map sqr: 1 .. 10'
(1 4 9 16 25 36 49 64 81 100)

The sqr function is part of the YS standard library. So is cube. But what about quarq (I just made that up) raising a number to the fourth power?

We could write a function for that:

$ ys -pe '
defn quarq(x): x ** 4
map quarq: 1 .. 10'
(1 16 81 256 625 1296 2401 4096 6561 10000)

But we could also write an anonymous function:

$ ys -pe 'map \(_ ** 4): 1 .. 10'
(1 16 81 256 625 1296 2401 4096 6561 10000)

The anonymous function syntax🔗

As you just saw, you create an anonymous function with the \( ... ) syntax.

Note

The \ is supposed to remind you of the λ symbol in lambda calculus which is all about anonymous functions. Haskell also uses \ for anonymous functions.

The _ is a symbol for the first argument. It can also be written as %1

Consider this example:

$ ys -pe 'apply \(%1 * %3 * %5): 1 .. 10'
15

We'll discuss apply in a future post, but notice how we multiplied the first, third, and fifth elements of the list 1 .. 10 without caring about the second or fourth elements?

The fn function🔗

The fn function defines a function. It's what defn uses except that you don't need to give it a name.

These all do the same thing:

defn quarq(x): x ** 4

quarq =: \(_ ** 4)

quarq =:
  fn(x): x ** 4

quarq =: fn([x] (x ** 4))

Higher order YS🔗

https://en.wikipedia.org/wiki/Higher-order_function

In mathematics and computer science, a higher-order function (HOF) is a function that does at least one of the following:

  • Takes one or more functions as arguments (i.e. a procedural parameter, which is a parameter of a procedure that is itself a procedure),
  • Returns a function as its result.

Let's write a function that returns a function.

Say we have a list of lengths in feet, meters, yards, or fathoms and we want to convert them to one of the other units.

We could write a function for each conversion or we could write a function that returns a function.

Convert a list of lengths from one unit to another

# convert,ys
!YS-v0

defn main(from to *nums):
  each x nums:
    converter =: converter-maker(from to)
    say: "$x $from is $(converter x) $to"

foot =::
  meters: 3.28084
  fathoms: 6.0
  feet: 1.0
  yards: 3.0

defn converter-maker(a b):
  fn(x):
    x / foot.$b *: foot.$a

The converter-maker function takes the names of 2 units and returns a function that converts a number from the first unit to the second unit.

Let's run it:

$ ys convert.ys fathoms meters 2 3 5 7 11 13
2 fathoms is 3.657599882956804 meters
3 fathoms is 5.486399824435206 meters
5 fathoms is 9.143999707392009 meters
7 fathoms is 12.801599590348811 meters
11 fathoms is 20.11679935626242 meters
13 fathoms is 23.774399239219225 meters

Fathom that!

Ok, it's Summer hammock time.

See you tomorrow!


Note

If you made it this far, don't forget to add a reaction and please leave a comment if you have something to ask or say. I'd appreciate it!

Comments