oberon:generics

Обобщённые модули в стандартном Oberon

Обобщённые модули (дженерики) — модули, параметризированные по константам, процедурам и типам без требования к генетичекой связи через расширение записей. Такое обобщение кода позволяет повысить либо его переиспользуемость, либо эффективность вместе со статическими гарантиями.

Существуют диалекты Oberon с явной поддержкой параметризуемых модулей, но сам по себе исходный язык не предусматривает явных конструкций параметризации. Но что интересно, даже без явной поддержки обобщённые модули всё равно могут использоваться как принцип за счёт самой модульности, требуя лишь расширенного понимания того, что стоит за IMPORT. Подход настолько прост, что может использоваться даже без инструментальной поддержки, хотя и не исключает её. Если при явном введении дженериков в Oberon используют обобщение понятия модуля, то здесь, наоборот, обобщённые модули рассматриваются как частный случай использования обычных модулей.

Здесь представлен схематичный пример. Дополнительные детали воплощения для достижения нужных качеств представить несложно.

Параметризация модуля

«Параметризированный» модуль может ничем не отличаться от обычного модуля, так как все необходимые объявления он берёт из импортированного модуля, который и служит ему параметром:

MODULE List; IMPORT Param;

 TYPE 
  T* = POINTER TO R;
  R = RECORD
    next: T;
    val*: Param.T
  END;
 
 PROCEDURE Insert*(VAR list: T; val: Param.T);
 VAR l: T;
 BEGIN
  NEW(l);
  l.next := list;
  l.val  := val;
  list   := l
 END Insert;

 PROCEDURE Next*(VAR item: T): BOOLEAN;
 BEGIN
  item := item.next
 RETURN
  item # NIL
 END Next;

END List.

Формальный и фактический параметры-модули

Следующий исходный(формальный) параметр-модуль не только служит заготовкой для фактических параметров-модулей, но также позволяет напрямую использовать исходный список как динамически типизированный, что тоже имеет смысл:

MODULE Param; IMPORT V;
 TYPE T* = POINTER TO V.Base;
END Param.

Фактический параметр-модуль с нужными объявлениями:

MODULE ParamRec;
 TYPE T* = RECORD r*: REAL; i*: INTEGER END;
END ParamRec.

При должном обобщённом подходе модули-параметры могут использоваться вместе с разными параметризированными модулями.

Специализация параметризированного модуля

Псевдо-код для специализации модуля где-то в сборочной системе:

FileTool.Replace("List.mod", "ListRec.mod",
  "'MODULE List;'->'MODULE ListRec', 'END List.'->'END ListRec.', 'IMPORT Param'->'IMPORT Param := ParamRec'")

ChatGPT4 понял смысл FileTool.Replace и предложил такой работающий код для POSIX-систем:

modspec() { sed -e "s/MODULE $1;/MODULE $2;/g" -e "s/END $1\./END $2\./g" -e "s/IMPORT $3/IMPORT $3 := $4/g" "$1.mod" > "gen/$2.mod"; }
modspec List ListRec Param ParamRec

Эта команда показывает возможность нулевого воплощения, в котором обобщенные модули используются без какой-либо специализированной поддержки, задействуя исключительно общеприменимые инструменты. При наличии отдельных утилит специализация может выглядеть так:

Modules.Spec("ListRec := List(Param := ParamRec)")

Использование специализированного модуля

MODULE UseList;

 IMPORT ListRec, Rec := ParamRec, log;

 PROCEDURE Go*;
 VAR list, item: ListRec.T; v: Rec.T;
 BEGIN
  list := NIL;

  v.i := 1; v.r := 2.3;  ListRec.Insert(list, v);
  v.i := 0; v.r := 1.2;  ListRec.Insert(list, v); 

  item := list;
  REPEAT
    log.i(item.val.i); log.s(" "); log.rn(item.val.r)
  UNTIL ~ListRec.Next(item)

 END Go;

END UseList.

Ссылки

Весь код в песочнице.

Источник

oberon/generics.txt · Последнее изменение: 2024/07/15 15:48 — comdiv