Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keyword "as" in SML/NJ

Tags:

sml

smlnj

I recently see people using as in their SML/NJ program. The most helpful reference I found is "as" keyword in OCaml.

Though OCaml also belongs to ML programming language family, they are different. For example, in the example program given in the previous answer,

let rec compress = function
    | a :: (b :: _ as t) -> if a = b then compress t else a :: compress t
    | smaller -> smaller;;

My translation of it to SML/NJ is (Please correct me if I have done it wrong)

fun compress (a :: (t as b :: _)) = if a = b then compress t else a :: compress t
  | compress smaller = smaller

As you have seen, the pattern (b :: _ as t) has an order different from (t as b :: _) in the second snippet. (Still, their usage is pretty much the same)

For potential answers, I hope it can contain (1) reference to this keyword as in any among the official documentation of SML/NJ, courses, and books and "maybe" (2) some examples to illustrate its usage. I am hoping this question can help future users seeing as.

like image 891
Tai Avatar asked Mar 06 '23 10:03

Tai


1 Answers

The as keyword is part of the Standard ML definition ('97 revision). See page 79, figure 22 (highlighting mine):

enter image description here

These are called as-patterns in Haskell and pretty much any other language that allows binding an identifier to a (sub-)pattern, but the origin of the name is clearly from ML.

The purpose it serves is to give a name to a pattern or a part of it. For example, we can capture the whole head of a 2-tuple list, while at the same assigning names to the tuple's values.

fun example1 (list : (int * string) list) =
  case list of
    (* `head` will be bound to the tuple value *)
    (* `i` will be bound to the tuple's 1st element *)
    (* `s` will be bound to the tuple's 2nd element *)
    head as (i, s) :: tail => ()
  | nil => ()

The other usage appears in record patterns. Notice that at first sight it might give the impression that the as-name is now to the right of the as keyword, but it's not (see the combined example below):

fun example2 (list : { foo: int * string } list) =
  case list of
    (* `f` will be found to the value of the `foo` field in the record. *)
    { foo as f } :: tail => ()

    (* The above can also be expressed as follows *)
  | { foo = f } :: tail => ()

  | nil => ()

And a combined example, where you can see that as usage in records is consistent with its usage elsewhere, i.e., the name stays on the left-hand side of the as keyword (here the name is the record label).

fun example3 (list : { foo: int * string } list) =
  case list of
    head as { foo as (i, s) } :: tail => ()

    (* This is valid, too, but `foo` is not a binding. *)
  | head as { foo = (i, s) } :: tail => ()

    (* Here, `f` is bound to the whole tuple value. *)
  | head as { foo = f as (i, s) } :: tail => ()

  | nil => ()
like image 127
Ionuț G. Stan Avatar answered Apr 07 '23 04:04

Ionuț G. Stan