Functions
A function is a piece of code with a name. Once declared, it can be run, or called, by writing its name, as many times as you like. Functions let you write something once and use it everywhere, and give a name to what a few lines of code do.
func greet() { print("Hello!")}
greet()greet()Hello!Hello!func declares a function, followed by its name, parentheses, and its code in braces. Declaring it
doesn’t run it; greet() does. You have been calling functions since the first page: print and
input are functions that come with Emerald.
Giving a function values
Section titled “Giving a function values”A function can take parameters: names for the values it needs, each with its type:
func greet(name: String) { print("Hello, #{name}!")}
greet("Ada")greet("Grace")Hello, Ada!Hello, Grace!The values in the call, "Ada" and then "Grace", are the arguments. Each call gives name the
argument it was passed. A function can take several parameters, separated by commas.
Emerald checks every call against the function’s parameters before the program runs, so a call with the wrong kind of value, or the wrong number of them, is caught:
func greet(name: String) { print("Hello, #{name}!")}
greet(42)functions.em:5:7: this is Int, but parameter `name` of `greet` needs String greet(42) ^^Pass a value of the expected type, or convert it first.Getting an answer back
Section titled “Getting an answer back”A function can work something out and return it. The type of the answer goes after the
parentheses, following a colon, and return gives the answer back:
func area(width: Int, height: Int): Int { return width * height}
print(area(3, 4))const room = area(5, 2)print(room)1210A call to area is a value like any other: it can be printed, stored, or used in a calculation.
return also ends the function at once, so anything after it doesn’t run.
Every way through a function that promises an answer has to give one. Here, a number that isn’t positive falls out of the bottom with nothing to return:
func sign(n: Int): String { if n > 0 { return "positive" }}functions.em:1:6: not every path in `sign` returns a value func sign(n: Int): String { ^^^^Add a `return` on every path, or restructure so every branch returns.Emerald can usually work out the answer’s type itself, so : Int can be left off area. Writing it
is a good habit anyway: it tells a reader what to expect without reading the code.
Returning early
Section titled “Returning early”A function can return from several places. The one-line return ... if form suits a function that
settles simple cases first:
func describe(age: Int): String { return "a baby" if age < 1 return "a child" if age < 13 return "a teenager" if age < 20 return "an adult"}
print(describe(0))print(describe(15))print(describe(40))a babya teenageran adultDefault values and named arguments
Section titled “Default values and named arguments”A parameter can have a default value, used when the call leaves it out. Parameters with defaults come after the ones without:
func greet(name: String, punctuation: String = "!") { print("Hello, #{name}#{punctuation}")}
greet("Ada")greet("Grace", "?")greet("Hopper", punctuation: "!!!")Hello, Ada!Hello, Grace?Hello, Hopper!!!The last call names its argument, punctuation: "!!!". Naming arguments makes a call easier to
read, especially when several are numbers: area(width: 3, height: 4) says which is which. Named
arguments can come in any order, but they go after any unnamed ones.
Each name declares one function. There can’t be two functions called show, one for numbers and one
for text; give them different names, or use a default value.
Where functions can go
Section titled “Where functions can go”A function can be called before the line it is declared on, so a program can put its main code at the top and its helper functions below:
greet()
func greet() { print("Hi from below")}Hi from belowParameters can’t be changed
Section titled “Parameters can’t be changed”A parameter is a constant inside the function; it holds the value it was given:
func bump(n: Int) { n += 1}functions.em:2:5: `n` cannot be reassigned n += 1 ^Parameters are read-only. Assign it to a local variable first if you need a version that can change.A list passed to a function is the function’s own copy, so a change to it would vanish when the function ends. Emerald refuses the change rather than let it silently disappear:
func add_guest(guests: List[String], guest: String) { guests.append(guest)}functions.em:2:5: `guests` is a parameter, so a change to it would be lost when the function returns guests.append(guest) ^^^^^^Copy it into a `var`, change the copy, and return it, as in `var changed = guests`.The fix is to return the changed list, and have the caller keep it:
func add_guest(guests: List[String], guest: String): List[String] { var updated = guests updated.append(guest) return updated}
var party = ["Ada"]party = add_guest(party, "Grace")print(party)["Ada", "Grace"]The page on classes introduces objects that are shared rather than copied, for when several parts of a program need to change the same thing.
A function that calls itself
Section titled “A function that calls itself”A function may call itself. This is called recursion, and suits problems that contain smaller copies of themselves:
func factorial(n: Int): Int { return 1 if n <= 1 return n * factorial(n - 1)}
print(factorial(5))120A recursive function that returns a value must state its return type, since Emerald can’t work it
out from a call to itself. Each call must get closer to a case that stops, here n <= 1; without
one, the calls would go on until Emerald stops them with a
RecursionError.
What you’ve learned
Section titled “What you’ve learned”func name(parameters) { ... }declares a function, andname(arguments)calls it.- Parameters have types, and Emerald checks every call.
: Typeafter the parentheses andreturngive back an answer; every path must return one.- Parameters can have defaults, and arguments can be named.
- A function can be called before its declaration.
- Parameters can’t be changed; return a changed copy instead.
- A function can call itself, if it states its return type and always reaches a stopping case.
Next, lambdas and blocks: functions without names, passed to other functions.