Re: Roles in C# 9. Нужно?
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.06.20 15:10
Оценка: 14 (1)
Здравствуйте, varenikAA, Вы писали:

AA>

Пока не очень понятно, нужно ли.
Соображения:
0. 90% из того, о чём он рассуждает — это сложный способ разрешить определять operator+ за пределами типа.
В плюсах этой проблемы нет как класса благодаря наличию свободных функций; в шарпе этого нет — в итоге имеем печальный итог: мы не можем описать оператор+ для аргументов типа A и B, не будучи авторами A или B.
1. Рассказ про то, что у инта сразу два моноида, был убедителен, но непонятно, как именно это можно применить. Ну, кроме вырожденного случая "давайте проагрегируем массив интов с использованием 1 и * в качестве нашего моноида".
Ну, вот например — давайте определим умножение матриц с элементами типа T. По идее, нам надо как-то "скормить" в наш метод MatrixMultiply(T[,] a, T[,] b) оба моноида.
Ну, ок, мы, допустим, вызываем его так:
MatrixMultiply<IntAddMonoid, IntMulMonoid>(int[,] a, int[,] b).

А что мы пишем в тельце функции? Непонятно. В примере Мэдса функция AddAll обходилась одним моноидом. Мы могли привести аргумент к IntAddMonoid и получить сложение, могли — к IntMulMonoid, и получить умножение.

interface IMonoid<T, R>
{
  static R Zero{get;}
  static R operator+(T t1, T t2) 
}

R[,] MatrixMultiply<M, A, R>(M[,] a, M[,] b)
  where M:IMonoid<M>
  where A:IMonoid<A>
{
    int rA = a.GetLength(0);
    int cA = a.GetLength(1);
    int rB = b.GetLength(0);
    int cB = b.GetLength(1);
    if (cA != rB)
        throw new InvalidOperationException("Argument size mismatch");
    double[,] result = new double[rA, cB];
    for (int i = 0; i < rA; i++)
        for (int j = 0; j < cB; j++)
        {
            A t = A.Zero;
            for (int k = 0; k < cA; k++)
            {
                A ab = a[i, k] + b[k, j]; // a & b are M, so the first monoid triggers
                t += ab;  // t & ab are A, so the second monoid triggers
            }
             result [i, j] = temp;
        }
    return result;
}

О май фрикин гад! Я специально расписал операции с временной переменной, т.к. иначе от знаков сложения начинает рябить в глазах.
Ну, и при вызове я не вижу способа обойтись без передачи третьего типа.
Так что целочисленные матрицы будут у нас умножаться через
int[,] a;
int[,] b;
var c = MatrixMultiply<IntMulMonoid, IntAddMonoid, int>(a, b)

В общем, кошмар на марше.
Не, я понял, что можно же напилить IntRing аналогичным образом, и иметь его и вдоль и поперёк. Но получается мы при выпиливании IntRing никак не можем повторно использовать заранее напиленные моноиды для сложения и умножения.
А это опять означает линейный рост объёма кода для покрытия более-менее всех интересных нам числовых типов.
В свете этого мне проще плюнуть и напилить умножение матриц через делегаты:
T[,] MatrixMultiply<T>(T[,] a, T[,] b, Func<T, T, T> mul, Func<T, T, T> add, T zero)
{
    int rA = a.GetLength(0);
    int cA = a.GetLength(1);
    int rB = b.GetLength(0);
    int cB = b.GetLength(1);
    if (cA != rB)
        throw new InvalidOperationException("Argument size mismatch");
    double[,] result = new double[rA, cB];
    for (int i = 0; i < rA; i++)
        for (int j = 0; j < cB; j++)
        {
            T t = zero;
            for (int k = 0; k < cA; k++)
                t = add(t, mul(a[i, k], b[k, j]));
             result [i, j] = temp;
        }
    return result;
}

Заодно этот код понятнее читается. Использовать его ничуть не сложнее шаманства с моноидами:
int[,] a;
int[,] b;
var c = MatrixMultiply(a, b, (x, y)=>x*y, (x, y)=>x+y, 0);


2. Вот екстеншны мне нравятся. Прежде всего возможностью напилить Extension property — потому что регулярно возникает желание напилить что-то вроде .Height или .Width для двумерных массивов, или Length для коллекций — по тем же правилам, что и обычные методы.
То есть если у нас тип умеет "родной" Length — как массив — то он и возвращается за O(1). А если нет — велкам в енумерцию, и не надо в каждом типе пилить реализацию.
И не надо вносить это в интерфейс как дефолтный метод.
И да — перегрузить оператор для чужого типа — тоже отличная идея.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.