An environment is an abstract data type, i.e., to say it again, a data type together with some operators that satisfy some conditions and which can only be used through these operators. The goal of an environment is to represent names and their denotation. So assuming the type:
type name = string;;
our goal here is to define the polymorphic type:
type 'a environment
together with the following operators:
An environment maps names (represented here as OCaml strings) to their denotation (represented here as OCaml values). An environment may be empty, it may result from extending an environment with a new binding, and it can be consulted by looking up a name.
Food for thought:
OCaml features two notions of environment:
(Looking up a name in OCaml does not return an optional value: if this name denotes a value in the current environment, this value is returned; otherwise, an error is raised.)
The environment operators should satisfy the following conditions:
The following unit-test function illustrates these conditions:
let test_environment_int empty extend lookup =
let b0 = (lookup "x" empty
= None)
and b1 = (lookup "x" (extend "x" 2 empty)
= Some 2)
and b2 = (lookup "x" (extend "x" 2 (extend "x" 3 empty))
= Some 2)
and b3 = (lookup "x" (extend "y" 2 (extend "x" 3 empty))
= Some 3)
and b4 = (lookup "z" (extend "y" 2 (extend "x" 3 empty))
= None)
(* etc. *)
in b0 && b1 && b2 && b3 && b4;;
Beef up test_environment_int with more tests and add error messages, along the lines of the After hours section of Week 08.
An association list is a list of pairs where each pair holds a name and the corresponding denotation.
When an environment is represented as an association list,
Programmatically:
type 'a environment_alist = (name * 'a) list;;
let empty_alist =
[];;
let extend_alist x d e =
(x, d) :: e;;
let lookup_alist x e =
let rec visit e =
match e with
| [] ->
None
| (x', d) :: e' ->
if x' = x
then Some d
else visit e'
in visit e;;
These OCaml functions pass the unit test:
# test_environment_int empty_alist extend_alist lookup_alist;;
- : bool = true
#
When an environment is represented as a function mapping a name to an optional denotation,
Programmatically:
type 'a environment_fun = name -> 'a option;;
let empty_fun =
fun x -> None;;
let extend_fun x d e =
fun x' -> if x = x'
then Some d
else e x';;
let lookup_fun x e =
e x;;
These OCaml functions pass the unit test:
# test_environment_int empty_fun extend_fun lookup_fun;;
- : bool = true
#
Alfrothul: The definition of lookup_fun is simple, but what a headache to come up with it.
Harald: Maybe we should have looked at its type.
Alfrothul: But we know its type, it is name -> 'a environment -> 'a option.
Harald: Yes it is, but here, 'a environment is name -> 'a option.
Alfrothul: So?
Harald: So the type of lookup_fun is name -> (name -> 'a option) -> 'a option.
Brynja: Hello, modus ponens. We meet again.
Alfrothul: You mean, as in the Interlude about functions applied to other functions in Week 05?
Brynja: Yup.
Harald: We need to be more than constantly vigilant. Linearly, perhaps?
Halcyon: Like Professor Moody would go all “LINEAR VIGILANCE!”?
Brynja: More like “QUADRATIC VIGILANCE!”.
Alfrothul: I kind of like “POLYNOMIAL VIGILANCE!”.
“Mad Eye” Moody: You all might as well stop mincing words – “EXPONENTIAL VIGILANCE!”
Created [24 Mar 2020]