Post1up 14-First Class
Post1up 14-First Class
Racket is a functional programming language, primarily because Racket’s functions are first
class values.
Functions have the same status as the other values we’ve seen. They can be:
Functions are first class values in the Intermediate Student (and above) versions of Racket.
Functions as first-class values have historically been missing from languages that are not
primarily functional.
The utility of functions-as-values is now widely recognized, and they are at least partially
supported in many languages that are not primarily functional, including C++, C#, Java, Go,
and Python.
Consuming functions M14 4/41
(foo + 2 3) ⇒ (+ 2 3) ⇒ 5
(foo * 2 3) ⇒ (* 2 3) ⇒ 6
Is this useful?
Consider two similar functions, eat-apples and keep-odds.
> Example: Eating apples M14 5/41
filter is an example of a higher order function. Higher order functions either consume a
function or produce a function (or both).
We’ll see more higher order functions in the next lecture module.
> Using filter M14 11/41
filter and other higher order functions provided in Racket are used to apply common
patterns of simple recursion.
We’ll discuss how to write contracts for them shortly.
Ex. 1 Use filter to write a function that keeps all multiples of 3.
(keep-multiples3 (list 1 2 3 4 5 6 7 8 9 10)) ⇒ (list 3 6 9)
Use filter to write a function that consumes a (listof Num) and keeps only values
between 10 and 30, inclusive.
Ex. 3
Write a function (sum-odds-or-evens lst) that consumes a (listof Int). If there are
Ex. 5
more evens than odds, the function returns the sum of the evens. Otherwise, it returns
the sum of the odds.
Use local.
> Advantages of functional abstraction M14 12/41
We saw in lecture module 14 how local could be used to create functions during a
computation, to be used in evaluating the body of the local.
But now, because functions are values, the body of the local can produce such a function
as a value.
Though it is not apparent at first, this is enormously useful.
We illustrate with a very small example.
> Example: make-adder M14 14/41
((make-adder 3) 4)
⇒ ((local [(define (f m) (+ 3 m))] f) 4)
⇒ (define (f_1 m) (+ 3 m)) (f_1 4)
⇒ (+ 3 4) ⇒ 7
Before Now
First position in an application First position can be an expression (computing the
must be a built-in or user-defined function to be applied). Evaluate it along with the
function. other arguments.
A function name had to follow an A function application can have two or more open
open parenthesis. parentheses in a row: ((make-adder 3) 4).
» A note on scope M14 16/41
In add3 the parameter m is of no consequence after add3 is applied. Once add3 produces its
value, m can be safely forgotten.
However, our earlier trace of make-adder shows that after it is applied the parameter n does
have a consequence. It is embedded into the result, f, where it is “remembered” and used
again, potentially many times.
> Producing and consuming functions M14 17/41
Using local to produce a function gives us a way to create semi-custom functions “on the
spot” to use in expressions. This is particularly useful with higher order functions such as
filter.
In the next lecture module we’ll see an easier way to produce functions that are only used
once – like eat-apples.
Write a function (make-divisible? n) that produces a predicate function. The
predicate function consumes a Int, returns true if its argument is divisible by n, and
false otherwise.
You may test your function by having it produce a function for filter:
Ex. 6
The result of make-adder can be bound to an identifier and then used repeatedly.
(add2 3) ⇒ 5
(add3 10) ⇒ 13
(add3 13) ⇒ 16
» Tracing a bound identifier M14 19/41
(add2 3)
⇒ (f_1 3)
⇒ (+ 2 3)
⇒5
Storing functions in lists & structures M14 20/41
Recall our code in lecture module 11 for evaluating arithmetic expressions (just + and * for
simplicity):
In opnode we can replace the symbol representing a function with the function itself:
(define-struct opnode (op args))
;; An opnode is a (make-opnode ??? (listof AExp))
;; An AExp is (anyof Num opnode)
(check-expect (eval 3) 3)
(check-expect (eval (make-opnode + (list 2 3 4))) 9)
(check-expect (eval (make-opnode + empty)) 0)
This works for any binary function that is also defined for zero arguments.
Next steps:
We know that a structure with n fields can be replaced with an n-element list.
For example:
(eval (list + 1 (list * 3 3 3)))
vs.
(eval (make-opnode + (list 1 (make-opnode * (list 3 3 3)))))
Examples:
'X is an abbreviation for (quote X). quote is a special form; it does not evaluate its
arguments in the normal fashion.
CS135 will use quoting to represent lists more compactly for this version of eval and
apply and to represent graphs in M18. We will not use it elsewhere and it will not be
tested.
Convert each value into quote notation, and enter the quoted version into DrRacket.
(Your solution should contain the quote symbol, ', but should not contain cons or list.)
If your quoted code is correct, DrRacket will convert it back to the same code in list
Ex. 7
notation.
1 (cons 4 (cons "Donkey" (cons 'ice-cream empty)))
2 (list 'paper 'pen "eraser" (list 32 'pencil (list "calculator")))
Recap M14 27/41
We’d like to use quoted lists to make the input to eval more natural:
However, quoting turns the + and * functions into symbols: '+ and '*.
Can we implement eval and apply without resorting to another cond and lots of boilerplate
code?
Yes: create a dictionary (association list) that maps each symbol to a function.
> Example: Functions in a table (1/2) M14 28/41
As a first class value, we can do anything with a function that we can do with other values.
We saw them all in the last example:
An application of (filter pred? lst) can work on any type of list, but the predicate
provided should consume elements of that type of list.
In other words, we have a dependency between the type of the predicate and the type of list.
To express this, we use a type variable, such as X, and use it in different places to indicate
where the same type is needed.
> The contract for filter M14 37/41
We have used type variables in contracts for a long time. For example, (listof X).
What is new is using the same variable multiple times in the same contract. This indicates a
relationship between parts of the contract. For example, filter’s list and predicate are
related.
We will soon see examples where more than one type variable is needed in a contract.
Many of the difficulties one encounters in using higher order functions can be overcome by
careful attention to contracts.
For example, the contract for the function provided as an argument to filter says that it
consumes one argument and produces a Boolean value.
This means we must take care to never use filter with an argument that is a function that
consumes two variables, or that produces a number.
Write a version of insertion sort, (isort pred? lst), which consumes a predicate and
a (listof X) and produces lst in sorted order.
For example, when called with the predicate odd? and one of the following GTs, the
function produces 129.
78 78
81 66 48 11 11 48 66 81
37 12 12 37
Goals of this module M14 40/41
You should understand the idea of functions as first-class values: how they can be
supplied as arguments, produced as values, bound to identifiers, and placed in lists and
structures.
You should understand how a function’s contract can be used as its type. You should
be able to write contracts for functions that consume and/or produce functions.
Summary: built-in functions M14 41/41
The following functions and special forms have been introduced in this module:
filter
You should complete all exercises and assignments using only these and the functions and
special forms introduced in earlier modules. The complete list is:
* + - ... / < <= = > >= abs add1 and append boolean? ceiling char-alphabetic?
char-downcase char-lower-case? char-numeric? char-upcase char-upper-case?
char-whitespace? char<=? char<? char=? char>=? char>? char? check-error check-expect
check-within cond cons cons? cos define define-struct define/trace e eighth else
empty? equal? error even? exp expt fifth filter first floor fourth integer? length
list list->string list? local log max min modulo negative? not number->string number?
odd? or pi positive? quotient remainder rest reverse round second seventh sgn sin
sixth sqr sqrt string->list string-append string-downcase string-length
string-lower-case? string-numeric? string-upcase string-upper-case? string<=? string<?
string=? string>=? string>? string? sub1 substring symbol=? symbol? tan third zero?