Классы типов в PascalABC.NET

Классы типов есть в Haskell. Можно погуглить.

Классы типов в PascalABC.NET пока ещё очень слабые и с багами. Надеюсь, баги будут устраняться.

Классы типов напоминают интерфейсы, но лучше. В них можно использовать операции. Вот пример для начала:

type 
  Less[T] = typeclass
    function operator<(x, y: T): boolean;
  end;
  Less[integer] = instance
    function operator<(x, y: integer): boolean := x<y;
  end;

function MinIndex<T>(a: array of T): integer; where Less[T];
begin
  Result := -1;
  var min := a[0]; 
  for var i:=0 to a.Length - 1 do
    if a[i]<min then
    begin
      Result := i;
      min := a[i];
    end;
end;

begin
  var a := Arr(10,3,5,7,8); 
  MinIndex(a).Println;
end.

Здесь вишенкой на торте является возможность использовать операцию < обобщённом коде. Это напоминает C++, но это не так. MinIndex будет работать только с массивами типа T, удовлетворяющего классу типов Less[T]

1 лайк

А почему [] а не <>?

И, что важнее, эта фича должна работать только с where или как? К примеру, я вот попробовал добавить так операторы в интерфейс:

type 
  TCComparable[T] = typeclass
    function operator=(x, y: T): boolean;
    function operator<>(x, y: T): boolean;
    function operator<(x, y: T): boolean;
    function operator<=(x, y: T): boolean;
    function operator>(x, y: T): boolean;
    function operator>=(x, y: T): boolean;
  end;
  
  I1=interface
    property Value:real read;
  end;
  
  TCComparable[I1] = instance
    function operator=(x, y: I1): boolean := x.Value=y.Value;
    function operator<>(x, y: I1): boolean := x.Value<>y.Value;
    function operator<(x, y: I1): boolean := x.Value<y.Value;
    function operator<=(x, y: I1): boolean := x.Value<=y.Value;
    function operator>(x, y: I1): boolean := x.Value>y.Value;
    function operator>=(x, y: I1): boolean := x.Value>=y.Value;
  end;

procedure p1<T>(o1,o2:I1);
where TCComparable[T];
begin
  writeln(o2>o1);//работает
end;

begin
  var o1,o2:I1;
  writeln(o2>o1);//а это нет
end.

Получается, TCComparable.operator> сработал только когда o1 и o2 отправили в процедуру с where. Это так и задумано?

Инстансы интерфейсов надо запретить. Это Issue кстати. Из теории известно, что там возникают проблемы с неоднозначностью вывода типов

Еще в классах типов может быть реализация:

type
  Ordering = (_EQ, _LT, _GT);

  Eq[T] = typeclass
    function operator=(x, y: T): boolean := not (x <> y);
    function operator<>(x, y: T): boolean := not (x = y);
  end;
  
  Ord[T] = typeclass(Eq[T])
    function compare(x, y: T): Ordering;
    begin
      if x = y then Result := _EQ
      else if less(x, y) then Result := _LT
      else Result := _GT;
    end;
    
    function Less(x, y: T): boolean := compare(x, y) = _LT;
    function Greater(x, y: T): boolean := compare(x, y) = _GT;
    function LessEqual(x, y: T): boolean := compare(x, y) <> _GT;
    function GreaterEqual(x, y: T): boolean := compare(x, y) <> _LT;

    function operator>(x, y: T): boolean := greater(x, y);
    function operator<(x, y: T): boolean := less(x, y);
    function operator>=(x, y: T): boolean := greaterEqual(x, y);
    function operator<=(x, y: T): boolean := lessEqual(x, y);
    
    function Min(x, y: T): T := x < y ? x : y;
    function Max(x, y: T): T := x > y ? y : x;
  end;

А можно подробнее?

И, всё равно, с классами тоже не работает:

type 
  tc1[T] = typeclass
    function operator>(a,b:T):boolean;
  end;
  
  t1=abstract class
    v:real;
  end;
  tc1[t1] = instance
    function operator>(a,b:t1):boolean := a.v>b.v;
  end;

begin
  var o1,o2:t1;
  writeln(o1>o2);
end.

Хотя, тут, конечно, уже можно в самом классе определить оператор. Но разница в том что его придётся 2 раза определить, если он используется и во where и не_шаблонным кодом.

Нет, Вы неправильно это применяете. Вызов операций класса типов возможен только внутри обобщенной функции, где на параметр T наложено ограничение

У меня вообще typeclass - не ключевое слово. Версия самая новая.

В смысле не ключевое – не выделяется жирным в редакторе? Тогда попробуйте установить вот этот файл PascalABCNET.xshd (11,7 КБ) с кастомными настройками (просто замените оригинальный в папке c:\Program Files (x86)\PascalABC.NET\Highlighting\).

Ну, это пока вообще экспериментальная возможность

1 лайк

Как и с неймспейсами - сначала компилятор, потом анализатор кода и только после этого Ctrl+Space и выделение жирным. Оно, кстати, ещё и не форматируется (из за анализатора кода). В общем да, стадия на столько же экспериментальная как и у namespace, но в отличии от него - typeclass активно дорабатывается.

Я лично противник namespaceов - их делал ibond - я вообще не понимаю, как и для чего это реализовано. С него и спрашивайте.

Вообще, я этот топик заводил не для того чтобы начинали критиковать, что что-то в продакшне не доработано.

Неймспейсы нужны. Для чего, думаю, понятно и так.

Если можно, пишите об этом в другой теме

Ок.

@admin объясните всё же проблему инстансов интерфейсов.

Интерфейсы это в основном ограниченые абстрактные классы, с только 1 личной фичей - их можно несколько сразу наследовать в 1 классе. Если взять это во внимание - я вижу только 1 случай с конфликтами:

type
  I1=interface end;
  I2=interface end;
  
  t1=class(I1,I2) end;
  
  tc1[T]=typeclass
    function f1:integer;
  end;
  
  tc1[I1]=instance
    function f1 := 1;
  end;
  tc1[I2]=instance
    function f1 := 2;
  end;

procedure p1<T>(a:T);
where tc1[T];
begin
  writeln(a.f1);
end;

begin
  p1(new t1)
end.

То есть, в случае когда в инстансах обоим интерфейсам есть одинаковая функция f1.

Вот только точно такой же конфликт появляется при 2 where:

type
  t1=class end;
  
  tc1[T]=typeclass
    function f1:integer;
  end;
  tc2[T]=typeclass
    function f1:integer;
  end;
  
  tc1[t1]=instance
    function f1 := 1;
  end;
  tc2[t1]=instance
    function f1 := 2;
  end;

procedure p1<T>(a:T);
where tc1[T];
where tc2[T];
begin
  writeln(a.f1);
end;

begin
  p1(new t1);
end.

Это ведь не повод запрещать 2 where?)) Надо выводить “несколько подпрограмм могут быть вызваны”, а не запрещать потенциально полезные конструкции.

Поправьте если я чего то не понял, вместо того чтоб продолжать игнорировать этот вопрос.

Про инстансы интерфейсов уже есть Issue. Повторять не буду - мы это запретим.

Про секцию с двумя where - я Ваш код не понимаю. он не работает с override-ошибкой.

Принципиальная ошибка вашего кода - Вы определяете в instance то, чего нет в классе типов, а это неправильно. Вторая принципиальная ошибка - Вы пользуетесь a.f1, хотя ни в одном классе типов нет f1.

Там сообщение об ошибке неудачное - да, спасибо: Program1.pas(8) : Нет метода для переопределения Оно дурацкое конечно. Но по сути правильно - нечего переопределять

1 лайк

2 issue*)))

Я и не прошу повторять. Я прошу объяснить почему, что именно с ними не так.

Прямо сейчас, в доступном билде, любой код выдаёт Undefined FileName, поэтому я по сути писал весь код без компилятора, вот и не заметил. Поправил. Но это не меняет сути, того что я пытался показать.

Ой, это была какая-то статья по теории типов - я честно говоря не помню, мы её года два назад на семинаре рассматривали.

Ну так а может она не револьвентна для этого языка? Я в интернете несколько раз искал, не нашёл ничего про несовместимость интерфейсов и классов типов.

Ну да. Потому что в языках, в которых есть классы типов, нет интерфейсов :slight_smile: