On this page:
:  :
:  :
:  :
:  ~
:  ~
:  ~
Any
Any.of
Any.to_  boolean
None
Any.like
Any.like_  element
Any.like_  key
Any.like_  value
Any.like_  first
Any.like_  rest
Any.like_  field
is_  a
is_  a
described_  as
matching
satisfying
converting
maybe
!!
!!
!!
!!.
!!.
?.
?.
8.17.0.1

5.4 Annotations🔗ℹ

expression

expr :: annot

 

repetition

repet :: annot

Checks that the value of expr satisfies annot, and returns the value if so.

> [1, 2, 3] :: List

[1, 2, 3]

binding operator

bind :: annot

Binds the same as bind, but first checks that the value to be bound satisfies annot.

If annot is a converter annotation, the conversion is applied before matching the converted value against bind. This dependency implies that the conversion in annot cannot be delayed, and must be performed as part of the matching process (before committing to the match).

> def x :: List = [1, 2, 3]

expression

expr :~ annot

 

repetition

repet :~ annot

Associates static information to the overall expression the same as ::, but performs no run-time check on the value of expr. The annot must specify a predicate annotation.

> [1, 2, 3] :~ List

[1, 2, 3]

> "oops" :~ List

"oops"

binding operator

bind :~ annot

Associates static information to bind the same as ::, but performs no run-time check. The annot must specify a predicate annotation.

> def x :~ List = [1, 2, 3]

> def x :~ List = "oops"

annotation

Any

 

annotation

Any.of(expr, ...)

 

annotation

Any.to_boolean

 

annotation

None

The Any annotation matches any value. An Any.of annotation matches any value that is equal (in the sense of ==) to one of the expr results. The Any.to_boolean annotation matches any value and converts non-#false value to #true.

The None annotation matches no values, or in other words, is equivalent to Any.of(). It is useful for asserting that something never returns, such as a function that always throws.

See Annotations from Predicates for information about the time that expr is evaluated.

> "hello" is_a Any

#true

> "hello" is_a Any.of("hello", "goodbye")

#true

> "hola" is_a Any.of("hello", "goodbye")

#false

> "hello" :: Any.to_boolean

#true

> #false :: Any.to_boolean

#false

> "will not match" is_a None

#false

> "will not match" is_a Any.of()

#false

annotation

Any.like(arg_id)

 

annotation

Any.like_element(arg_id)

 

annotation

Any.like_key(arg_id)

 

annotation

Any.like_value(arg_id)

 

annotation

Any.like_first(arg_id)

 

annotation

Any.like_rest(arg_id)

 

annotation

Any.like_field(class_name . field_id(arg_id))

Annotation constructors for use in a context where named arguments are available—especially in the result annotation position of fun. Annotations created by Any.like and related forms do not imply any run-time checks, but they propagate static information from actual argument expressions in a specific function call to that specific call’s result.

In the case of a method, this can be used as an arg_id to refer to the actual target object. When an arg_id refers to a repetition, then static information for actual arguments mapped to the repetition are combined with statinfo_meta.or. When an arg_id refers to a splice or keyword splice, then corresponding arguments are similarly combined with statinfo_meta.or to get the element or value static information within the splice variable as a list or map.

An Any.like annotation propagates static information directly from an argument.

fun twice(v) :: List.of(Any.like(v)):

  [v, v]

fun rev(v, ...) :: List.of(Any.like(v)):

  [v, ...].reverse()

> use_static

> def lst = twice("apple")

> lst[0].length() // method found statically

5

> rev("a", "bb", "ccc")[0].length() // ditto

3

An Any.like_element annotation propagates static information corresponding to sequence or indexable values within an argument.

fun pick(choices :: List) :: Any.like_element(choices):

  choices[math.random(choices.length())]

> use_static

> pick(["a", "b"]).length()

1

The Any.like_key or Any.like_value annotation constructors are normally used for maps. They are similar to Any.like_element, but for a sequence that produces two values, where Any.like_key corresponds to the first value and Any.like_value the second.

fun listize(m :: Map) :: List.of(Pair.of(Any.like_key(m),

                                         Any.like_value(m))):

  for List ((k, v) in m):

    Pair(k, v)

> use_static

> def lst = listize({ "a": [1, 2, 3], "b": [0, 0] })

> lst[0].first.length()

1

> lst[1].rest.reverse()

[3, 2, 1]

The Any.like_first or Any.like_rest are similar to Any.like_element, but for the components of pairs.

fun pick_part(pr :: Pair) :: Any.like_first(pr) || Any.like_rest(pr):

  if math.random(2) == 0

  | pr.first

  | pr.rest

> use_static

> pick_part(Pair("apples", "banana")).length()

6

An Any.like_field(class_name.field_id(arg_id)) annotation propagates static information corresponding to the field named by field_id in the class named by class_name, where arg_id is an object that is an instance of class_name.

class Posn(x :: Int || Flonum, y :: Int || Flonum):

  // if both are Int or both are Flonum, result is the same

  method dist() :: (Any.like_field(Posn.x(this))

                      || Any.like_field(Posn.y(this))):

    x + y

> 3.0 + Posn(1.0, 2.0).dist() // uses Flonum arithmetic

6.0

> 3.0 + Posn(1, 2.0).dist() // uses generic arithmetic

6.0

expression

expr is_a annot

 

repetition

repet is_a annot

 

expression

expr !is_a annot

 

repetition

repet !is_a annot

 

~order: equivalence

Produces #true if the value of expr satisfies annot, #false otherwise. The operator combination !is_a inverts the test. Either form works as a repetition given a repetition to test.

If annot is a converter annotation, only the matching component of the annotation is used, and the converting part is not used. See also Annotations as Converters.

> [1, 2, 3] is_a List

#true

> "oops" is_a List

#false

binding operator

bind described_as (term ...)

 

binding operator

bind described_as term ...

Equivalent to bind, but when a binding match fails in a way that triggers an error message (as opposed to moving on to a different binding pattern), the message describes the expected annotation as term .... The term ... sequence is not parsed, so it can be any sequence of terms, but the first term can be parenthesized only if the term ... sequence is parenthesized.

> def (x :: Int) described_as An Integer = "oops"

def: value does not satisfy annotation

  value: "oops"

  annotation: An Integer

annotation

matching(bind)

Converts bind into an annotation. Variables bound in bind are not made visible, but the annotation corresponds to the set of values for which bind would match. Since no results are made visible, bind is used only in matching mode, and implied conversions might be skipped.

> def x :: matching([_, 10]) = [9, 10]

> def y :: matching([_, 10]) = [9, 11]

def: value does not satisfy annotation

  value: [9, 11]

  annotation: matching([_, 10])

Produces a predicate annotation using the resulting function from pred_expr.

See Annotations from Predicates for information about the time that pred_expr is evaluated.

fun is_multiple_of(n):

  fun (v):

    v is_a Int && v mod n == 0

> 15 :: (satisfying(is_multiple_of(3))

           && satisfying(is_multiple_of(5)))

15

fun

| is_list_with_one(lst :: List):

    lst.contains(1)

| is_list_with_one(_):

    #false

> [1, 2, 3] :: satisfying(is_list_with_one)

[1, 2, 3]

> Array(1, 2, 3) :: satisfying(is_list_with_one)

::: value does not satisfy annotation

  value: Array(1, 2, 3)

  annotation: satisfying(is_list_with_one)

annotation

converting(fun (bind) maybe_res_annot:

             body

             ...)

 

maybe_res_annot

 = 

:: annot

 | 

:~ annot

 | 

ϵ

Produces a converter annotation by pairing bind with body. The annotation matches when bind matches, but the value produced by the annotation is determined by the body sequence, which can refer to variables bound by bind.

When annot is provided, then its static information is propagated to the new converter annotation. If annot is supplied with ::, then the result of the body sequence is checked against annot.

See also Annotations as Converters.

> def x :: converting(fun (x :: Int): x + 1) = 11

> def x :: converting(fun (x :: Int): x + 1) = "eleven"

def: value does not satisfy annotation

  value: "eleven"

  annotation: converting(fun (x :: Int): x + 1)

annotation

maybe(annot)

Equivalent to False || annot, which is an annotation that is satisfied by either #false or a value that satisfies annot. If annot is a converter annotation, its conversion applies to a non-#false value.

> #false :: maybe(String)

#false

> "string" :: maybe(String)

"string"

> #true :: maybe(String)

::: value does not satisfy annotation

  value: #true

  annotation: maybe(String)

expression

expr !!

 

binding operator

bind !!

 

repetition

repet !!

An an expression, expr!! ensures that the result of expr is not #false by throwing an exception if the result is #false. If expr has static information from maybe(annot), then the overall !! expression gets the static information of annot.

As a binding, bind!! matches non-#false values that match bind. Similar to the !! expression form, when static information for the input to bind!! is maybe(annot), then bind more specifically starts with the static information of annot.

> "apple"!!

"apple"

> #false!!

!!: claimed not false, but actual value is false

fun len(str :: maybe(String)):

  use_static

  match str

  | s!!: s.length()

  | ~else: 0

> len("apple")

5

> len(#false)

0

> len(1)

len: argument does not satisfy annotation

  argument: 1

  annotation: maybe(String)

expression

expr !!.

 

repetition

repet !!.

 

~order: member_access

The !!. operator is equivalent to !! followed by ., but without a space in between the operators.

fun len(str :: maybe(String)):

  use_static

  str!!.length() || 0

> len("apple")

5

> len(#false)

!!.: claimed not false, but actual value is false

expression

expr ?. id (arg, ...)

 

expression

expr ?. id

 

repetition

repet ?. id (arg, ...)

 

repetition

repet ?. id

 

~order: member_access

If expr produces #false, then the result of a ?. expression is #false. Otherwise, ?. is like . for a field access or method call.

When expr has static information from maybe(annot), then the argument to . has static information of annot. If the result of the . form in the non-#false case has static information like annot, then the overall ?. expression has static information like maybe(annot).

fun len(str :: maybe(String)):

  use_static

  str?.length() || 0

> len("apple")

5

> len(#false)

0