package yuujinchou

  1. Overview
  2. Docs

Yuujinchou is an OCaml package of name patterns. It was motivated by the "import" or "include" statements present in almost all programming languages. Here are a few examples:

open import M -- Agda
import foo # Python

The ability to import content from other files helps organize code. However, it also poses a new challenge: how could programmers prevent imported content from shadowing existing content? For example, if we already have a function test in the current scope, maybe we do not wish to import another function also named test. To address this, many programming languages allow programmers to selectively hide or rename part of the imported content:

open import M renaming (a to b) public
-- renaming a to b, and then re-exporting the content
import foo as bar
# putting content of foo under the prefix bar

We can view hiding and renaming as partial functions from names to names. I took this aspect seriously and designed a powerful (possibly overkilling) combinator calculus to express such partial functions---the library you are checking now. It supports renaming, scopes, sequencing, logical connectives, negation and tags with only six combinators in the language. For technical detail, see core.

Organization

The code is split into two parts:

module Pattern : sig ... end

The Pattern module defines the patterns.

module Action : sig ... end

The Action module implements the engine running the patterns.

How to Use It

Import Statements

open Yuujinchou

type data = int

(** An environment is a mapping from paths to data. *)
type env = (Pattern.path, data) Hashtbl.t

(** [remap pattern env] uses the [pattern] to massage
    the environment [env]. *)
let remap pattern env =
  let compiled_pattern = Result.get_ok @@ Action.compile_ pattern in
  let new_env = Hashtbl.create @@ Hashtbl.length env in
  begin
    env |> Hashtbl.iter @@ fun path data ->
    match Action.run_ compiled_pattern path with
    | `NoMatch -> ()
    | `Matched l -> l |> List.iter @@ fun (path, ()) ->
      match Hashtbl.find_opt new_env path with
      | None -> Hashtbl.replace new_env path data
      | Some data' ->
        if data <> data' then
          failwith "Inconsistent data assigned to the same path."
  end;
  new_env

(** [import env pattern imported] imports the environment
    [imported] massaged by [pattern] into [env]. *)
let import env pattern imported =
  Hashtbl.replace_seq env @@ Hashtbl.to_seq @@ remap pattern imported

Data Selection

open Yuujinchou

type data = int

(** An environment is a mapping from paths to data. *)
type env = (Pattern.path, data) Hashtbl.t

module DataSet = Set.Make (struct type t = data let compare = compare end)

let collect_matched env pattern =
  let compiled_pattern = Result.get_ok @@ Action.compile_ pattern in
  begin
    env |> Hashtbl.fold @@ fun path data set ->
    match Action.run_ compiled_pattern path with
    | `NoMatch -> set
    | `Matched _ -> DataSet.add data set
  end DataSet.empty

Namespace?

This library intends to treat a namespace as the prefix of a group of names. That is, there is no namespace a, but only a group of unrelated names that happen to have the prefix a.

Note that namespaces (name prefixes of unrelated items) are different from modules (groups of items that are bound together). This library does not provide special support for modules (yet).

Examples from Other Languages

Haskell

import Mod -- x is available an both x and Mod.x
join [any; renaming_prefix [] ["Mod"]]
import Mod (x,y)
join [only ["x"]; only ["y"]]
import qualified Mod
join [renaming_prefix [] ["Mod"]]
import qualified Mod hiding (x,y)
renaming_scope [] ["Mod"] @@ meet [hide ["x"]; hide ["y"]]

Racket

(require (only-in ... id0 [old-id1 new-id1]))
seq_filter [...; join [only ["id0"]; renaming ["old-id1"] ["new-id1"]]]
(require (except-in ... id0 id1]))
seq_filter [...; except ["id0"]; except ["id1"]]
(require (prefix-in p: ...))
seq [...; renaming_prefix [] ["p:"]]
(require (rename-in ... [old-id0 new-id0] [old-id1 new-id1]))
seq [...; join [renaming ["old-id0"] ["new-id0"]; renaming ["old-id1"] ["new-id1"]]]
(require (combine-in require-spec0 require-spec1 ...))
join [require-spec0; require-spec1; ...]

The provide mechanism can be simulated in a similar way. This library does not directly support the phase levels in Racket (yet).

What is "Yuujinchou"?

"Yuujinchou" is the transliteration of "友人帳" in Japanese, which literally means "book of friends". It is a powerful notebook in the manga Natsume Yuujinchou (夏目友人帳) that collects many real names (真名) of youkais (妖怪) (supernatural and spiritual monsters). These real names can be used to summon and control youkais, but the protagonist decided to return the names to their original owners. The plot is about meeting all kinds of youkais.

This magical book will automatically turn to the page with the correct name when the protagonist pictures the youkai in his mind. This library is also about finding real names of youkais.

The transliteration is in the Wāpuro style to use only English alphabet letters; otherwise, its Hepburn romanization would be "Yūjin-chō".