Contexts

Using contexts

Contexts allow values to be implicitly passed down through calls.

main void() the language = greeting: "Hello" # 'greet-three-times' implicitly takes 'the language' log greet-three-times "world" language record greeting string greet-three-times string(name string) language # 'greet' implicitly takes 'the language' join " ", n-of 3, greet name greet string(name string) language "{the::language.greeting}, {name}!"

Notice how the body of greet-three-times didn't have to mention the context.
The function signature did though; contexts aren't completely implicit.

There are only 2 operations on contexts:

  • the t = ... adds a context. The type annotation is required.
  • the::t gets the context. The type annotation is required.

When you call a function that needs the context (like greet-three-times or greet above), Keen checks that the context is available in the caller.
When greet-three-times calls greet, the context is available due to its own declared context.
That is the normal case of using a context: Just implicitly passing it along.

Contexts are most effective when there is the t = ... at the top, some the::t accesses at the bottom, and a lot of functions in between that just pass it along implicitly.

Contexts and specs

A spec can inherit from a context. This allows bundling multiple contexts (and specs) into a single spec.

main void() the language = greeting: "Hello" the punctuation = terminator: "!" log greet-three-times "world" language record greeting string punctuation record terminator string contexts spec language, punctuation greet-three-times string(name string) contexts join " ", n-of 3, greet name greet string(name string) contexts "{the::language.greeting}, {name}{the::punctuation.terminator}"