Инструменты пользователя

Инструменты сайта


oberon:generics

Это старая версия документа!


Обобщённые модули в стандартном 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.1720305549.txt.gz · Последнее изменение: 2024/07/07 01:39 — comdiv