Re[3]: Моноиды
От: hi_octane Беларусь  
Дата: 08.06.20 10:16
Оценка: 51 (4) +7
_>>И добавил моноиды в презентацию — чтобы хипстеры и менеджеры были хэппи.
Q>При чём тут хипстеры и тем более менеджеры;
Хипстеры тут при том что буквально каждый изучив эти самые монады и моноиды и решив что всё понял, бежит писать в блог свою версию объяснения, и оценочное суждение в духе "это офигенно". Учитывая что система типов C# мягко говоря не вывозит, такое информационное поле создаёт на создателей языка вполне ощутимое давление.

Менеджеры тут при том что они выделяют ресурсы на фичи. И легче всего им продать то что 1)хайповое 2)есть в другом языке которые "competitor".

Q>моноиды — обычная конструкция для инженеров.

Конструкция необычная. В моей практике (может просто так повезло с проектами) — людям сначала нравится элегантность и единообразность монад, моноидов и этого всего. Команда проникается крутостью подхода, всё как-бы очень красиво и работа кипит. Конструкции распространяются по коду. И через какое-то время процесс разработки встаёт колом. На любую задачу уровня "добавьте отдельный таймаут для операции N, и чтобы его можно было менять в конфиге" начинает уходить прорва времени неадекватная задаче и квалификации людей которые работают с монадным кодом. Анализ трэйсов и дампов становится мучением — там почти всегда просто сотни вложенных bind и apply, через которые могут продраться не только лишь все и мало кто хочет это делать. Люди просто забивают на любой способ отладки кроме гадания по логам. Когда дело доходить до оптимизации производительности или потребления памяти — часто проще всю монадную магию выкинуть и переписать с нуля на чистой императивщине.

При этом роли (шейпы) будут вполне полезны без всяких монад. Так что к ним претензий никаких.
Re: Классы типов для бедных
От: Qbit86 Кипр
Дата: 10.06.20 09:06
Оценка: 130 (3) +4
Здравствуйте, varenikAA, Вы писали:

AA>Roles in C# 9. Нужно?


Признаться, я не очень понял, чем эти «роли» отличаются от того, что раньше питчилось под названием «шейпы», кроме синтаксиса. И не нахожу их особенно полезными. Тем не менее предприму ещё одну попытку раскрыть упомянутый ранее пример, где эти роли можно применить.

Предположительно, они были бы полезны в тех случаях, где обобщённые алгоритмы выражаются в терминах политик, а не стратегий. Что это всё такое и зачем это нужно?

Стратегии — это как стратегии в книжке «Gang of Four». Кондовый ООП джава-стайл. Например, IEqualityComparer<TKey> в конструкторе Dictionary<TKey, TValue> — это стратегия.

Политики — это примерно как политики в книжке Александреску про C++. Как стратегии, но времени компиляции. В случае словаря это была бы передача в конструктор и захват «TKeyComparer where TKeyComparer : IEqualityComparer<TKey>» вместо IEqualityComparer<TKey>.

Безотносительно ролей и шейпов, такой дизайн стандартной коллекции дал бы следующий профит.

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

Во-вторых, тип компаратора явно становится частью типа словаря вместо тобы, чтобы быть стёртым и спрятанным за ширмой интерфейса IEqualityComparer<TKey>. Это позволяет избежать класса ошибок вроде: мы мерджим два словаря на строках, но, оказывается, первый словарь использует внутри компаратор InvariantCulture, а второй IgnoreCase, а снаружи это никак не видно.

Если у нас такой стиль API, то внедрение шейпов/ролей теоретически могло бы немного снизить синтаксическую нагрузку. Но скорее даже не с пользователей API, а с автора. А автору это не так критично, кмк.
Глаза у меня добрые, но рубашка — смирительная!
Re[7]: Оптимизация производительности
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 03:19
Оценка: +5
Здравствуйте, Qbit86, Вы писали:

Q>Зд

Q>Внезапно, код с моноидом в стиле как на видео будет быстрее, чем стандартная лапша с делегатами : )

Q>Рассмотрим моноид из видео (C# пятилетней давности подойдёт, ничего нового из превью 9.0). Осторожно, возможна интоксикация жутким хтоническим матаном в мозг!


Q>Бенчмарк можно взять отсюда и убедиться самостоятельно.

Простите, коллега, но этот аргумент ничтожен.
Во-первых, вы что, всерьёз предлагаете чинить проблемы перформанса рантайма при помощи ажно новой фичи языка?
Да, мы все в курсе, что вызов делегата — ещё хуже, чем косвенный вызов через интерфейс. Ну так это надо чинить там, где сломано — во-первых, нужно вернуть обратно выкинутую в первой версии поддержку single-cast делегатов, а во-вторых, наконец вынуть голову из того места, где она сейчас, и прикрутить спекулятивные оптимизации. Если вам непонятно, что именно я имею в виду — напишите, я расшифрую пошагово.
Почему этот способ лучше? Да потому, что он улучшит работу всего существующего кода, в частности весь linq 2 objects. А не только тех полуторых тысяч гипотетических строк кода, которые будут написаны с использованием новых фич C#10.

Во-вторых, не смешите мои тапочки. Если вы уж взялись рассуждать о производительности, за baseline надо брать не моноид, а простой прямолинейный код:
public int AddIntegers(int[] ints)
{
  int result=0;
  foreach(var i in ints)
    result+=i;
  return result
}

Вот к такой производительности надо стремиться. Заменять одну тормозную абстракцию на чуть-чуть менее тормозную — это даже не паллиатив, а профанация.
Я могу написать код, который делает обобщённый static T Reduce<T, TMonoid>(this IEnumerable<T> items, TMonoid monoid) быстрее, чем AddIntegers выше (без учёта времени прогрева), но это упраженение, которое я бы не стал заставлять делать каждого гражданского разработчика.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 09.06.2020 5:53 Sinclair . Предыдущая версия .
Re[6]: Оптимизация производительности
От: Qbit86 Кипр
Дата: 08.06.20 14:14
Оценка: 19 (2) +1 :)
Здравствуйте, hi_octane, Вы писали:

_>Ну так ты до этого не понял при чём там хипстеры и менеджеры


Так ведь это ж ты не понял, чем отличается монада от хламидомонады : ) С менеджерами невпопад, так как для них наоборот работает: если моноид — что-то сложное из матана, то всё, пора из инженеров в менеджеры.

_>Темы связаны потому что эти штуки друг без друга не ходят.




_>Когда дело доходить до оптимизации производительности или потребления памяти — часто проще всю монадную магию выкинуть и переписать с нуля на чистой императивщине.


Внезапно, код с моноидом в стиле как на видео будет быстрее, чем стандартная лапша с делегатами : )

Рассмотрим моноид из видео (C# пятилетней давности подойдёт, ничего нового из превью 9.0). Осторожно, возможна интоксикация жутким хтоническим матаном в мозг!
public interface IMonoid<T>
{
    T Identity { get; }
    T Combine(T left, T right);
}

internal readonly struct AdditiveInt32Monoid : IMonoid<int>
{
    public int Identity => 0;
    public int Combine(int left, int right) => left + right;
}

internal readonly struct MultiplicativeDecimalMonoid : IMonoid<decimal>
{
    public decimal Identity => 1m;
    public decimal Combine(decimal left, decimal right) => left * right;
}


И напишем свёртку, аналог Linq Aggregate:
internal static T Reduce<T, TMonoid>(this IEnumerable<T> items, TMonoid monoid)
    where TMonoid : IMonoid<T>
{
    if (items is null)
        throw new ArgumentNullException(nameof(items));

    T result = monoid.Identity;
    foreach (T item in items)
        result = monoid.Combine(result, item);

    return result;
}


Вызовы выглядят так:
[Benchmark(Baseline = true)]
public int ReduceIntegers() => s_integers.Reduce(default(AdditiveInt32Monoid));

[Benchmark]
public int AggregateIntegers() => s_integers.Aggregate(0, (left, right) => left + right);

[Benchmark]
public decimal ReduceDecimals() => s_decimals.Reduce(default(MultiplicativeDecimalMonoid));

[Benchmark]
public decimal AggregateDecimals() => s_decimals.Aggregate(1m, (left, right) => left * right);


Бенчмарк можно взять отсюда и убедиться самостоятельно.
Глаза у меня добрые, но рубашка — смирительная!
Re[11]: Baseline
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 07:51
Оценка: 7 (1) +3
Здравствуйте, Qbit86, Вы писали:

Q>Нет, это параметеры дженерика. В пользовательском графе веса рёбер могут быть int'ами, decimal'ами, Complex'ами, кастомными типами — чем угодно, заранее неизвестно. Мне нужно абстрагироваться от мономорфных плюсиков.

Ну вот я ровно об этом вам и говорю — то, чего мы хотим, так это чтобы AddAll(TWeight<double>[] weights) работал не хуже, чем AddAll(double[] weights).
Не имеет никакого смысла рассуждать о том, что AddAll(TWeight<double>[] weights, Func<TWeight<double>, TWeight<double>, TWeight<double>> add, TWeight<double> zero) работает хуже, чем AddAll(TWeight<double>[] weights, IMonoid<TWeight<double>> add). Если нас беспокоит производительность, то оба — отстой. Если не беспокоит, то и первого достаточно.

S>>Ну так он же справедлив. В конце концов мы выкидываем не только делегатов, но и интерфейсы, сворачивая цепочку map/reduce в конкретный метод, который вычисляет конкретное замыкание над конкретной коллекцией.

Q>Так ведь нет конкретной коллекции. Речь про написание библиотечного кода, который работает с любыми типами. В момент написания кода ещё не известны типы конечного пользователя. Нет такой альтернативы как «обычный нативный код сложения и умножения».
Вот именно об этом и речь. У нас есть чудесная библиотека, которая описывает поиск минимального пути на графе в терминах другой чудесной библиотеки универсальной агрегации и удачно описанных моноидов.
И это чудесно работает в proof of concept. А при переезде в продакшн конечные пользователи чертыхаются и заменяют всю эту кунсткамеру на плоский FindShortestPath с double и встроенными операциями; а потом ещё и переписывают на SIMD.
Потому что у них-то типы уже известны. Проблема как раз в том, что рантайм и язык плохо выполняют специализацию и её приходится выполнять вручную.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Айсберг
От: Qbit86 Кипр
Дата: 08.06.20 10:52
Оценка: -1 :)))
Здравствуйте, hi_octane, Вы писали:

_>каждый изучив эти самые монады и моноиды


Анекдот про тебя:

— Не люблю евреев!
— Почему?
— Они потопили «Титаник».
— Так ведь «Титаник» потопил айсберг?
— Да какая разница: АЙСБЕРГ, ВАЙСБЕРГ, ГРИНБЕРГ. Не люблю евреев!
Глаза у меня добрые, но рубашка — смирительная!
Re: Roles in C# 9. Нужно?
От: Silver_S Ниоткуда  
Дата: 26.07.21 09:58
Оценка: 96 (3)
Здравствуйте, varenikAA, Вы писали:

AA>Roles in C#


В данный момент оно в таком состоянии: [Proposal]: Static abstract members in interfaces
Re[2]: Моноиды
От: Qbit86 Кипр
Дата: 08.06.20 09:15
Оценка: +3
Здравствуйте, hi_octane, Вы писали:

_>И добавил моноиды в презентацию — чтобы хипстеры и менеджеры были хэппи.


При чём тут хипстеры и тем более менеджеры; моноиды — обычная конструкция для инженеров.

У него, правда, неправильно названы члены шейпа: вместо Zero и + должны быть Identity и Combine(). Чтоб не ломать мозг для мультипликативного моноида, где Zero это 1, а + это *.

_>Да нужны.


Вообще да, но раньше и без них обходились, называя их policy.
Глаза у меня добрые, но рубашка — смирительная!
Re[16]: mov
От: Qbit86 Кипр
Дата: 09.06.20 22:36
Оценка: 118 (2)
Здравствуйте, Sinclair, Вы писали:

S>Нет. Если внимательно посмотреть в мой ответ про перформанс, то окажется, что там приведён вот такой код:


А если внимательно посмотреть родительский комментарий, то там приведён другой код.

S>Это вы всё время стараетесь искусственно замедлить забег Ахилла


А ты пытаешься навязать мне какую-то другую задачу. Хочешь итерироваться по массиву? Окей, тогда сначала материализуй исходный IEnumerable<T> в массив, и ходи по нему. Например, рёбра остовного дерева не в массиве приходят, а генерируются в процессе обхода графа.

Q>>Алгоритмы-то всё-таки пишутся не только над массивами, а над произвольными структурами (деревья, графы общего вида, etc).


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

S>обычной проблемой обобщённых алгоритмов такого вида является то, что они работают плохо и на массивах.


Насколько я вижу, отлично работают. Лучше, чем можно было бы ожидать. И компилятор в CIL, и JIT из CIL.

S>Во-первых, давайте замерим хотя бы на десятке тысяч интов.


Окей, исправил в бенчмарке на 10 тысяч. Хоть это и не так важно, имхо; если нужна большая точность и статистическая значимость, то можно просто RunMode.Short исправить на RunMode.Long.

S>Во-вторых, использование i != items.Length вместо i < items.Length добавляет range check к каждой итерации. Интересно, что для линейного массива он вроде бы не сказывается на быстродействии.


Окей, даже хоть и не сказывается, заменим на foreach, как в твоём варианте.

S>В-третьих, удивительно, но факт: JIT всё же порождает разный код для generic случая и для специализации. Если починить проблему с !=, то generic версия начинает работать вот так:

S>
S>| Special                                  | Generic  
S>+------------------------------------------+-------------------------------------------
S>| 7FF85A3A41A7 movsxd    r9,edx            | 7FF85A3B3ED7 movsxd    r9,edx
S>|                                          | 7FF85A3B3EDA mov       r9d,[rcx+r9*4+10]
S>| 7FF85A3A41AA add       eax,[rcx+r9*4+10] | 7FF85A3B3EDF add       eax,r9d
S>| 7FF85A3A41AF inc       edx               | 7FF85A3B3EE2 inc       edx
S>


У меня разницы нет; эта промежуточная инструкция есть в обоих вариантах 🤷 Такое окружение:
.NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT


S>Вот эта лишнаяя инструкция ест 7% времени:


У меня на RunMode.Long такой результат:
|                 Method |       Mean |     Error |    StdDev |     Median | Ratio | RatioSD | Code Size |
|----------------------- |-----------:|----------:|----------:|-----------:|------:|--------:|----------:|
| ReduceIntegersBaseline |   4.879 us | 0.0074 us | 0.0385 us |   4.879 us |  1.00 |    0.00 |     131 B |
|         ReduceIntegers |   4.851 us | 0.0121 us | 0.0621 us |   4.876 us |  0.99 |    0.02 |     151 B |
|      AggregateIntegers |  74.457 us | 0.1019 us | 0.5166 us |  74.364 us | 15.26 |    0.15 |     419 B |
Глаза у меня добрые, но рубашка — смирительная!
Roles in C# 9. Нужно?
От: varenikAA  
Дата: 08.06.20 08:05
Оценка: 18 (1) -1
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[4]: Моноиды
От: AlexRK  
Дата: 08.06.20 14:31
Оценка: 8 (1) +1
Здравствуйте, hi_octane, Вы писали:

_>Конструкция Анализ трэйсов и дампов становится мучением — там почти всегда просто сотни вложенных bind и apply, через которые могут продраться не только лишь все и мало кто хочет это делать. Люди просто забивают на любой способ отладки кроме гадания по логам. Когда дело доходить до оптимизации производительности или потребления памяти — часто проще всю монадную магию выкинуть и переписать с нуля на чистой императивщине.


+1. Есть один знакомый архитектурный астронавт, у которого все через монады, бинды, апплу, эндофункторы, инверсии зависимостей и прочие подобные субстанции. Умножить на многословность и убогость системы типов сишарпа — и вуаля, блюдо готово. Реально код читать просто невозможно. Понять, что он делает — тем более. Блин, там, где нужно просто тупо вызвать form.ShowDialog, наверчено столько дерьма, что уму нерастяжимо. Видно, парень старается над job security. Если он свалит, весь этот кусок дерьма придется просто выкинуть.
Re[7]: default(TMonoid)
От: rameel https://github.com/rsdn/CodeJam
Дата: 09.06.20 14:05
Оценка: 8 (2)
Здравствуйте, Qbit86, Вы писали:

Q>Интуитивно кажется, что это не так, и «default(TMonoid)» должен быть эффективнее, чем «new TMonoid()» для создания экземпляров value-типов вроде decimal в generic-коде. Хотя бы из-за Activator.CreateInstance().

Q>Но точно не знаю, так что побенчмаркал. В .NET Core < 3.0 действительно new TMonoid() заметно медленнее. Но в .NET Core 3.0+ уже разницы нет. Activator.CreateInstance() по-прежнему есть в IL, но, видимо, JIT как-то лучше оптимизирует.

Да, джит устраняет Activator.CreateInstance<T>() если T — это value type Github: Optimize Activator.CreateInstance
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[14]: Baseline
От: Qbit86 Кипр
Дата: 09.06.20 09:16
Оценка: 111 (1)
Здравствуйте, Sinclair, Вы писали:

S>Надо не IEnumerable, а int[] items. Там 50% времени — это вызовы в GetNext.


Эта уловка называется «moving the goalspots» [1] [2]
Алгоритмы-то всё-таки пишутся не только над массивами, а над произвольными структурами (деревья, графы общего вида, etc).

Но ладно! Начав писать бенчмарки, сложно остановиться.

internal static T Reduce<T, TMonoid>(this T[] items, TMonoid monoid)
    where TMonoid : IMonoid<T>
{
    if (items is null)
        throw new ArgumentNullException(nameof(items));

    T result = monoid.Identity;
    for (int i = 0; i != items.Length; ++i)
        result = monoid.Combine(result, items[i]);

    return result;
}

internal static int ReduceInt32(this int[] items)
{
    if (items is null)
        throw new ArgumentNullException(nameof(items));

    int result = 0;
    for (int i = 0; i != items.Length; ++i)
        result += items[i];

    return result;
}


|                 Method |       Mean |     Error |    StdDev | Ratio | RatioSD | Code Size |
|----------------------- |-----------:|----------:|----------:|------:|--------:|----------:|
| ReduceIntegersBaseline |   7.737 ns | 0.4087 ns | 0.0224 ns |  1.00 |    0.00 |     139 B |
|         ReduceIntegers |   7.409 ns | 0.4154 ns | 0.0228 ns |  0.96 |    0.00 |     162 B |
|      AggregateIntegers |  85.019 ns | 4.5889 ns | 0.2515 ns | 10.99 |    0.04 |     419 B |
|         ReduceDecimals | 132.324 ns | 9.0149 ns | 0.4941 ns | 17.10 |    0.07 |     314 B |
|      AggregateDecimals | 171.322 ns | 4.9471 ns | 0.2712 ns | 22.14 |    0.07 |     595 B |
Глаза у меня добрые, но рубашка — смирительная!
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). А если нет — велкам в енумерцию, и не надо в каждом типе пилить реализацию.
И не надо вносить это в интерфейс как дефолтный метод.
И да — перегрузить оператор для чужого типа — тоже отличная идея.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Моноид и компаратор
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 02:47
Оценка: 7 (1)
Здравствуйте, Qbit86, Вы писали:

Q>Алгоритмы, использующие обобщённое сравнение всем известны: сортировки, деревья, хэш-таблицы. Использование компаратора ни у кого удивления не вызывает.

Q>Алгоритмы, использующие обобщённое сложение никому кроме меня не известны: например, алгоритм Дейкстры на графах. Поэтому использование моноида вызывает бурю эмоций.
Не, никакой бури не вызывает. То же самое матричное умножение вполне можно использовать для вычисления транзитивного замыкания на матрице смежности, если * заменить на &&, а + — на ||.
Или для поиска кратчайших путей — тот же Дийкстра, только для всех пар вершин сразу.

Q>Для обобщённого сравнения ты реализуешь IComparer<T> или IEqualityComparer<T>. Аналогично, для обобщённого сложения (обобщённой бинарной операции) ты реализуешь IMonoid<T>. Или, если ты больше не инженер, а уже менеджер, то IGenericAggregationControllerManager<T>, так трудовому народу понятнее.

Это всё как раз понятно.

S>>1. Рассказ про то, что у инта сразу два моноида, был убедителен, но непонятно, как именно это можно применить. Ну, кроме вырожденного случая "давайте проагрегируем массив интов с использованием 1 и * в качестве нашего моноида".


Q>Давай не про инты, а про float'ы, ок? Аддитивный моноид понятно, например, складывать веса дуг вдоль пути на графе. Мультипликативный моноид, например, когда у тебя цепочка процентных ставок: начинаем с 1.0, заплатили налог 13% (домножить на 0.87), получили interest 5% (домножить на 1.05) и так далее.

А какая разница? Хоть decimal. Ничто не мешает нам строить моноид хоть над string — в нём же даже коммутативность не нужна.
Я говорю о том, что предлагаемый вариант ролей не шибко помогает.

Q>Точно так же, как у тебя могут быть разные компараторы для одного типа (сравнение строк с учётом регистра, без учёта культуры, etc.), так и моноиды могут быть разные для одного типа.


Q>Взя затея вокруг шейпов — это добавить чуть синтаксического сахара вокруг всех этих IEqualityComparer<T>'ов и IMonoid<T>'ов, чтоб передавать не инстансы (они ж все одинаковые stateless), а сами типы.

Рулится прямо сейчас:
interface IMonoid<T>
{
   T Zero {get;}
   T Add(T t1, T t2)
}

public class Add<M>
  where M: IMonoid<T>, new
{
  public static T All<T>(T[] ts)
  {
   var m = new M();
   var T result = m.Zero;
   foreach(vat t in ts)
     result = m.Add(result, t);
  }
}

class IntAddMonoid: IMonoid<int>
{
  public int Zero{get=>0;}
  public int Add(int t1, int t2)=>t1+t2;
}

int sixtyThree = Add<IntAddMonoid>.All(new[]{1, 2, 4, 8, 16, 32});

С учётом того, что статические интерфейсы ты ничем не параметризуешь, дефолтного конструктора вполне достаточно.
Вся разница — код работает прямо сейчас, не дожидаясь C# 12.

Что было бы ценно — возможность биндить моноиды, кольца и группы к типам в обобщённом коде без костылей.
Чего мы хотим? Чтобы AddAll просто работал просто из коробки, с любыми Numeric-типами, включая Complex и прочую экзотику. Вот extension для этого — отличная идея.
А с ролями — не вижу смысла. Чтобы можно было использовать AddAll прямо в таком виде для того, чтобы вычислять минимум ряда, передав Min(a, b) в качестве +?
Простите, не вижу ценности по сравнению с простым кодом выше. Ну, ок — если добавить статические интерфейсы, то можно будет убрать строчку var m = new M(), и просто обращаться к M.Zero и M.Add.
С моей точки зрения, для обобщённой операции сложения использовать оператор "+" — баловство.
Операторы удобны там, где есть более-менее сложные формулы. Ну, там (a[i-1]+2*a[i]+a[i+1])/4. Вот такое переписывать в стиле "вызов метода" — плохо, теряется читаемость.
И как раз такие формулы имеют смысл для обобщённых numeric типов. И как раз в них сложения и умножения — это сложения и умножения, а не "обобщённые сложения".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: default(TMonoid)
От: Qbit86 Кипр
Дата: 09.06.20 13:37
Оценка: 7 (1)
Здравствуйте, Sinclair, Вы писали:

Q>>Даже конструктор не нужен (его констрейнт и вызов), достаточно default(M) с констрейнтом struct. В той статье «Concept C#: Type Classes for the Masses» именно такой подход рассматривался.

S>Тот же конструктор, вид в профиль — построен на автоматической доступности default constructor для структур.

Интуитивно кажется, что это не так, и «default(TMonoid)» должен быть эффективнее, чем «new TMonoid()» для создания экземпляров value-типов вроде decimal в generic-коде. Хотя бы из-за Activator.CreateInstance().
Но точно не знаю, так что побенчмаркал. В .NET Core < 3.0 действительно new TMonoid() заметно медленнее. Но в .NET Core 3.0+ уже разницы нет. Activator.CreateInstance() по-прежнему есть в IL, но, видимо, JIT как-то лучше оптимизирует.
Глаза у меня добрые, но рубашка — смирительная!
Re[9]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.06.20 08:31
Оценка: 7 (1)
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, Ночной Смотрящий, Вы писали:


НС>>А в DLR совместимых языках в основном структурная типизация в том или ином виде есть.


S>И у чем тогда проблема подтолкнуть утиную типизацию для интерфейсов в шарп?

Так роли это и делают.
Просто роли позволяют переопределить статические методы . Если не переопределил берутся текущие
и солнце б утром не вставало, когда бы не было меня
Re[8]: Оптимизация производительности
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 26.07.21 11:57
Оценка: 4 (1)
Здравствуйте, varenikAA, Вы писали:

Ну не совсем они эффективно итераторы используют
http://rsdn.org/forum/flame.comp/7996919.1
Автор: Serginio1
Дата: 24.04.21
и солнце б утром не вставало, когда бы не было меня
Re: Roles in C# 9. Нужно?
От: hi_octane Беларусь  
Дата: 08.06.20 08:23
Оценка: +1
AA>
Так это он шейпы переименовал. И добавил моноиды в презентацию — чтобы хипстеры и менеджеры были хэппи. Так легче видимо эти шейпы будет пропихнуть.

Да нужны. Нам их ещё в C#9 обещали.
Re[5]: Straw man
От: Qbit86 Кипр
Дата: 08.06.20 14:43
Оценка: +1
Здравствуйте, AlexRK, Вы писали:

ARK>+1. Есть один знакомый архитектурный астронавт, у которого все через монады, бинды, апплу, эндофункторы, инверсии зависимостей и прочие подобные субстанции.


Типичный straw man.

Впрочем, это легко решается. Достаточно переименовать IMonoid<T> в IGenericAggregationControllerManager<T>, как привычно простым менеджерАм, которые против этого вашего матана и рокет сайенс. Гляди-ка, совсем другое дело, специально для вас с hi_octane:
public interface IGenericAggregationControllerManager<T>
{
    T Identity { get; }
    T Combine(T left, T right);
}
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Моноид и компаратор
От: Qbit86 Кипр
Дата: 08.06.20 15:47
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>0. 90% из того, о чём он рассуждает — это сложный способ разрешить определять operator+ за пределами типа.

S>В плюсах этой проблемы нет как класса благодаря наличию свободных функций; в шарпе этого нет — в итоге имеем печальный итог: мы не можем описать оператор+ для аргументов типа A и B, не будучи авторами A или B.

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

Алгоритмы, использующие обобщённое сравнение всем известны: сортировки, деревья, хэш-таблицы. Использование компаратора ни у кого удивления не вызывает.
Алгоритмы, использующие обобщённое сложение никому кроме меня не известны: например, алгоритм Дейкстры на графах. Поэтому использование моноида вызывает бурю эмоций.

Для обобщённого сравнения ты реализуешь IComparer<T> или IEqualityComparer<T>. Аналогично, для обобщённого сложения (обобщённой бинарной операции) ты реализуешь IMonoid<T>. Или, если ты больше не инженер, а уже менеджер, то IGenericAggregationControllerManager<T>, так трудовому народу понятнее.

S>1. Рассказ про то, что у инта сразу два моноида, был убедителен, но непонятно, как именно это можно применить. Ну, кроме вырожденного случая "давайте проагрегируем массив интов с использованием 1 и * в качестве нашего моноида".


Давай не про инты, а про float'ы, ок? Аддитивный моноид понятно, например, складывать веса дуг вдоль пути на графе. Мультипликативный моноид, например, когда у тебя цепочка процентных ставок: начинаем с 1.0, заплатили налог 13% (домножить на 0.87), получили interest 5% (домножить на 1.05) и так далее.

Точно так же, как у тебя могут быть разные компараторы для одного типа (сравнение строк с учётом регистра, без учёта культуры, etc.), так и моноиды могут быть разные для одного типа.

Взя затея вокруг шейпов — это добавить чуть синтаксического сахара вокруг всех этих IEqualityComparer<T>'ов и IMonoid<T>'ов, чтоб передавать не инстансы (они ж все одинаковые stateless), а сами типы. Примерно, как в этой старой статье: https://github.com/MattWindsor91/roslyn/blob/master/concepts/docs/csconcepts.md
Глаза у меня добрые, но рубашка — смирительная!
Отредактировано 08.06.2020 15:50 Qbit86 . Предыдущая версия .
Re[8]: IEqualityComparer<T>
От: Qbit86 Кипр
Дата: 08.06.20 18:03
Оценка: +1
Здравствуйте, hi_octane, Вы писали:

_>Нет. Это ты сначала написал что не понял при чём там хипстеры и менеджеры и я объяснил. Потом ты не понял при чём там монады, и я объяснил.


А я объяснил, почему твоё объяснение некорректное. Про straw man не только к AlexRK относилось, но и к тебе в первую очередь; почитай, пожалуйста.

_>Сейчас ты решил что всё понял.


Да, я всё понял: ты слышал звон, но не знаешь, где он. M-word — эта сложна! Нипанятна! А, речь не про этот M-word?.. Всё равно от лукавого, буквы-то те же! Нафиг не нужон моноид ваш!

Q>>Внезапно, код с моноидом в стиле как на видео будет быстрее, чем стандартная лапша с делегатами : )


Хоть по этому вопросу возражений нет.

_>Если мы про производительность в реальной жизни — то иногда случается что в стиле "у нас всё алгебраично" пишут алгоритм сложнее однострочника. Даже на твоём примере — допустим начали с суммы, а потом понадобилось ещё min и max посчитать. В императивном стиле найдут те 3 строчки кода, добавят ещё две и посчитают всё за один проход. В Моноидах знаешь что будет? Сделают ещё парочку моноидов, и вызовут их. И не потому что диверсанты, а просто логика подхода такая. При этом формально всё по фэншую, и сложность останется O(n). А по факту три прохода по памяти далеко не равны одному.

_>И через год продуктивной работы в таком стиле мы приходим к ситуации когда всё очень красиво. Но безбожно тормозит. И при этом какого-то конкретного боттлнека нету. Открываешь профайлер — а там в топах эти apply, reduce, и т.п. То один лишний проход по памяти, то два. И каждый раз спектр возможных оптимизаций сводятся к отказу от абстракций и объединению мелких кусков в более крупные, написанные в machine-friendly стиле (читай императивно).

Если при использовании IMonoid<T> непременно такие сложности возникнут, то они же неизбежно должны возникнуть и при использовании интерфейса IEqualityComparer<T>? Сценарии использования у них ведь строго одинаковые.
Или ты из тех же соображений отказался от IEqualityComparer<T>?
Глаза у меня добрые, но рубашка — смирительная!
Re[4]: default(TMonoid)
От: Qbit86 Кипр
Дата: 09.06.20 06:43
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Не, никакой бури не вызывает.


Это у тебя не вызывает, а я говроил не про тебя. В соседней ветке ещё как вызывает.

S>Я говорю о том, что предлагаемый вариант ролей не шибко помогает.


Согласен. Просто вся ветка перешла от обсуждения полезности «ролей», к обсуждению полезности абстракции моноида.

S>Рулится прямо сейчас:


Всё так.

S>
var m = new M();

S>С учётом того, что статические интерфейсы ты ничем не параметризуешь, дефолтного конструктора вполне достаточно.

Даже конструктор не нужен (его констрейнт и вызов), достаточно default(M) с констрейнтом struct. В той статье «Concept C#: Type Classes for the Masses» именно такой подход рассматривался.

S>Вся разница — код работает прямо сейчас, не дожидаясь C# 12.


Всё верно! (Только я в своих API предпочитаю по старинке протаскивать такие policies явно — более гибко. Но действительно, можно и не параметром метода, а параметром дженерика с самостоятельным инстанцированием через default(T).)

S>С моей точки зрения, для обобщённой операции сложения использовать оператор "+" — баловство.


Категорически согласен.
Глаза у меня добрые, но рубашка — смирительная!
Отредактировано 09.06.2020 6:49 Qbit86 . Предыдущая версия .
Re[13]: Baseline
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 08:56
Оценка: +1
Здравствуйте, Qbit86, Вы писали:
Q>Методы бенчмарка:
Q>[cs][Benchmark(Baseline = true)]
Q>public int ReduceIntegersBaseline() => s_integers.ReduceInt32();

Надо не IEnumerable, а int[] items. Там 50% времени — это вызовы в GetNext.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Baseline
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 15:23
Оценка: +1
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, Sinclair, Вы писали:


S>>Надо не IEnumerable, а int[] items. Там 50% времени — это вызовы в GetNext.


Q>Эта уловка называется «moving the goalspots» [1] [2]

Нет. Если внимательно посмотреть в мой ответ про перформанс, то окажется, что там приведён вот такой код:
public int AddIntegers(int[] ints)
{
  int result=0;
  foreach(var i in ints)
    result+=i;
  return result
}

Это вы всё время стараетесь искусственно замедлить забег Ахилла

Q>Алгоритмы-то всё-таки пишутся не только над массивами, а над произвольными структурами (деревья, графы общего вида, etc).

Это как раз понятно; но обычной проблемой обобщённых алгоритмов такого вида является то, что они работают плохо и на массивах.

Q>Но ладно! Начав писать бенчмарки, сложно остановиться.


Q>
internal static T Reduce<T, TMonoid>(this T[] items, TMonoid monoid)
Q>    where TMonoid : IMonoid<T>
Q>{
Q>    if (items is null)
Q>        throw new ArgumentNullException(nameof(items));

Q>    T result = monoid.Identity;
Q>    for (int i = 0; i != items.Length; ++i)
Q>        result = monoid.Combine(result, items[i]);

Q>    return result;
Q>}

Q>internal static int ReduceInt32(this int[] items)
Q>{
Q>    if (items is null)
Q>        throw new ArgumentNullException(nameof(items));

Q>    int result = 0;
Q>    for (int i = 0; i != items.Length; ++i)
Q>        result += items[i];

Q>    return result;
Q>}


Q>
Q>|                 Method |       Mean |     Error |    StdDev | Ratio | RatioSD | Code Size |
Q>|----------------------- |-----------:|----------:|----------:|------:|--------:|----------:|
Q>| ReduceIntegersBaseline |   7.737 ns | 0.4087 ns | 0.0224 ns |  1.00 |    0.00 |     139 B |
Q>|         ReduceIntegers |   7.409 ns | 0.4154 ns | 0.0228 ns |  0.96 |    0.00 |     162 B |
Q>|      AggregateIntegers |  85.019 ns | 4.5889 ns | 0.2515 ns | 10.99 |    0.04 |     419 B |
Q>|         ReduceDecimals | 132.324 ns | 9.0149 ns | 0.4941 ns | 17.10 |    0.07 |     314 B |
Q>|      AggregateDecimals | 171.322 ns | 4.9471 ns | 0.2712 ns | 22.14 |    0.07 |     595 B |
Q>

Во-первых, давайте замерим хотя бы на десятке тысяч интов. Для десяти целых почти любой способ будет приемлемым, даже если складывать их передавая в javascript.
Во-вторых, использование i != items.Length вместо i < items.Length добавляет range check к каждой итерации. Интересно, что для линейного массива он вроде бы не сказывается на быстродействии. Для 2d массивов замедление весьма существенное — может быть,
В-третьих, удивительно, но факт: JIT всё же порождает разный код для generic случая и для специализации. Если починить проблему с !=, то generic версия начинает работать вот так:
| Special                                  | Generic  
+------------------------------------------+-------------------------------------------
| 7FF85A3A41A7 movsxd    r9,edx            | 7FF85A3B3ED7 movsxd    r9,edx
|                                          | 7FF85A3B3EDA mov       r9d,[rcx+r9*4+10]
| 7FF85A3A41AA add       eax,[rcx+r9*4+10] | 7FF85A3B3EDF add       eax,r9d
| 7FF85A3A41AF inc       edx               | 7FF85A3B3EE2 inc       edx



Вот эта лишнаяя инструкция ест 7% времени:
|                 Method |      Mean |     Error |    StdDev | Ratio | RatioSD | Code Size |                                                                                                                        |----------------------- |----------:|----------:|----------:|------:|--------:|----------:|
| ReduceIntegersBaseline |  6.846 us |  1.775 us | 0.0973 us |  1.00 |    0.00 |     128 B |
|       ReduceIntegersRC |  6.787 us |  2.507 us | 0.1374 us |  0.99 |    0.01 |     139 B |
|         ReduceIntegers |  7.309 us | 10.276 us | 0.5632 us |  1.07 |    0.10 |     151 B |
|      AggregateIntegers | 69.800 us | 78.744 us | 4.3162 us | 10.20 |    0.73 |     419 B |
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Оптимизация производительности
От: Sharov Россия  
Дата: 09.06.20 20:57
Оценка: :)
Здравствуйте, Sinclair, Вы писали:


S>
S>public int AddIntegers(int[] ints)
S>{
S>  int result=0;
S>  foreach(var i in ints)
S>    result+=i;
S>  return result
S>}
S>

.
S>Я могу написать код, который делает обобщённый static T Reduce<T, TMonoid>(this IEnumerable<T> items, TMonoid monoid) быстрее, чем AddIntegers выше (без учёта времени прогрева), но это упраженение, которое я бы не стал заставлять делать каждого гражданского разработчика.

Будте добры, а то не верится что будет быстрее. Уж больно T Reduce<T, TMonoid>(this IEnumerable<T> items, TMonoid monoid) абстрактный, со всеми косвенностями.
Кодом людям нужно помогать!
Re[4]: Моноид и компаратор
От: dmitry_npi Россия  
Дата: 13.06.20 08:54
Оценка: +1
Здравствуйте, все, вы писали [много]

Я извиняюсь, а чего хотелось-то, массив просуммировать, что ли?

Если что —
Атмосферная музыка — www.aventuel.net
Re[2]: Roles in C# 9. Нужно?
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.06.20 18:35
Оценка: +1
Здравствуйте, hi_octane, Вы писали:

_>Так это он шейпы переименовал. И добавил моноиды в презентацию — чтобы хипстеры и менеджеры были хэппи. Так легче видимо эти шейпы будет пропихнуть.


Исходно это все от классов типов Хаскеля растет, отсюда и моноиды.

_>Да нужны. Нам их ещё в C#9 обещали.


По мне так данная реализация убога. Нужно делать как в Go, т.е. чтобы вместо обычного интерфейса можно было был передать любой объект у которого есть все члены этого интерфейса (совпадающие по имени + сигнатура). Тогда этот костыль будет не нужен.

А так получается половинчатое решение. А половинчатое оно потому, что делается средствами компилятора, в то время как для полноценной реализации нужна поддержка рантайма.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Моноиды
От: Qbit86 Кипр
Дата: 08.06.20 10:44
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>Хипстеры тут при том что буквально каждый изучив эти самые монады и моноиды и решив что всё понял

_>В моей практике (может просто так повезло с проектами) — людям сначала нравится элегантность и единообразность монад
_>начинает уходить прорва времени неадекватная задаче и квалификации людей которые работают с монадным кодом.
_>Анализ трэйсов и дампов становится мучением — там почти всегда просто сотни вложенных bind и apply
_>часто проще всю монадную магию выкинуть и переписать с нуля на чистой императивщине.
_>При этом роли (шейпы) будут вполне полезны без всяких монад.

🤦‍♂️ Myötähäpeä

При чём тут монады я вообще не понял. Похожее по созвучию, что ли? Такую простыню накатать на абсолютно постороннюю тему; реально стыдно за людей, которые плюсуют такое. Если от самой простой алгебраической структуры «моноид» так подгорает, то что случится при встрече с ужасным словом «группа»?
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.20 10:46
Оценка:
Здравствуйте, hi_octane, Вы писали:

AA>>

_>Так это он шейпы переименовал. И добавил моноиды в презентацию — чтобы хипстеры и менеджеры были хэппи. Так легче видимо эти шейпы будет пропихнуть.

_>Да нужны. Нам их ещё в C#9 обещали.


Я так понял главное там в том что в интерфейсе можно объявить static op_ и
и role для специализации
моноид это просто название интерфейса
и солнце б утром не вставало, когда бы не было меня
Re[5]: Моноиды
От: hi_octane Беларусь  
Дата: 08.06.20 11:28
Оценка:
Q>При чём тут монады я вообще не понял.
Ну так ты до этого не понял при чём там хипстеры и менеджеры

Q>Похожее по созвучию, что ли? Такую простыню накатать на абсолютно постороннюю тему;

Темы связаны потому что эти штуки друг без друга не ходят. Как только проект вырастет из хелло ворлд — понадобится из одних моноидов вызывать другие с сохранением какого-то контекста. И здрасьте вам, понадобились монады.

Q>Если от самой простой алгебраической структуры «моноид» так подгорает, то что случится при встрече с ужасным словом «группа»?

Как говорят в Одессе, мы таки покупаем или продаём? Если собеседование в модный стартапчик, то я готов с горящими глазами обсуждать группы, полугруппы, кольца, моноиды, функторы и всю эту дребедень. Но если мы таки обсуждаем практику — то у меня за последние много лет ощущение что чем серьёзнее в проекте ставка на "простые алгебраические структуры", тем больше у ПМ-а/инвестора вероятность через год обнаружить что "купил слона".
Re[3]: Моноид и компаратор
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 08.06.20 16:26
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Проблема определить оператор сложения вне типа никак и ничем принципиально не отличается от проблемы определить оператор сравнения вне типа. Это ровно та же проблема для ровно тех же задач — написание обобщённых алгоритмов и структур данных, которые используют полиморфные сложения и сравнения.


Q>Алгоритмы, использующие обобщённое сравнение всем известны: сортировки, деревья, хэш-таблицы. Использование компаратора ни у кого удивления не вызывает.

Q>Алгоритмы, использующие обобщённое сложение никому кроме меня не известны: например, алгоритм Дейкстры на графах. Поэтому использование моноида вызывает бурю эмоций.

Q>Для обобщённого сравнения ты реализуешь IComparer<T> или IEqualityComparer<T>. Аналогично, для обобщённого сложения (обобщённой бинарной операции) ты реализуешь IMonoid<T>. Или, если ты больше не инженер, а уже менеджер, то IGenericAggregationControllerManager<T>, так трудовому народу понятнее.


В том-то и дело. Многие программисты могут представить зачем им понадобится обощенное сравнение. Даже если им на практике никогда не доводилось создаавать код, где такое срвнение нужно (у нас же есть .OrderBy).
Но практически никто не может представить зачем нужно обобщенное сложение. Такое мало того, что никто не писал, мало кто представляет зачем оно нужно хотя бы в теории.
Зато все понимают кост новой фичи языка — лишние ключевые слова и совместимость с существующим, более сложное чтение и навигация по коду. Неожиданные сайд-эффекты от лишнего юзинга.

Нужен другой пример использования ролей, который будет более понятен всем. Я думаю эта фича, как и куча последних фич, родилась из разработки самого компилятора C#. Могли бы сделать пример с ним, а не с моноидами.
Re[7]: Оптимизация производительности
От: hi_octane Беларусь  
Дата: 08.06.20 16:43
Оценка:
_>>Ну так ты до этого не понял при чём там хипстеры и менеджеры
Q>Так ведь это ж ты не понял, чем отличается монада от хламидомонады : )
Нет. Это ты сначала написал что не понял при чём там хипстеры и менеджеры и я объяснил. Потом ты не понял при чём там монады, и я объяснил. Сейчас ты решил что всё понял. И сразу про хламидомонады. "Можно я уже пойду?"

Q>Внезапно, код с моноидом в стиле как на видео будет быстрее, чем стандартная лапша с делегатами : )

Ну если для сложения и умножения надо писать стандартную лапшу с делегатами...

* Код выкинут за ненадобностью.
Все и так знают что в C# делегаты сделаны через одно место. Генерировали бы они при создании лямбды интерфейс в одним методом .Invoke (как это делает один всем известный язык), и оба подхода работали бы одинаково быстро.

Если мы про производительность в реальной жизни — то иногда случается что в стиле "у нас всё алгебраично" пишут алгоритм сложнее однострочника. Даже на твоём примере — допустим начали с суммы, а потом понадобилось ещё min и max посчитать. В императивном стиле найдут те 3 строчки кода, добавят ещё две и посчитают всё за один проход. В Моноидах знаешь что будет? Сделают ещё парочку моноидов, и вызовут их. И не потому что диверсанты, а просто логика подхода такая. При этом формально всё по фэншую, и сложность останется O(n). А по факту три прохода по памяти далеко не равны одному.
И через год продуктивной работы в таком стиле мы приходим к ситуации когда всё очень красиво. Но безбожно тормозит. И при этом какого-то конкретного боттлнека нету. Открываешь профайлер — а там в топах эти apply, reduce, и т.п. То один лишний проход по памяти, то два. И каждый раз спектр возможных оптимизаций сводятся к отказу от абстракций и объединению мелких кусков в более крупные, написанные в machine-friendly стиле (читай императивно).
В общем я это всё сразу и написал
Автор: hi_octane
Дата: 08.06.20
. Но стоит в объяснении упустить какую-то промежуточную мелочь, и ты сразу решаешь что я что-то не понимаю. Всё наоборот. Когда я эту магию плохо понимал — недостатков вообще не видел, кругом виделись одни сплошные плюсы.
Отредактировано 08.06.2020 16:44 hi_octane . Предыдущая версия .
Re[2]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.20 19:02
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

S>var c = MatrixMultiply<IntMulMonoid, IntAddMonoid, int>(a, b)


Я вот только не понял зачем IntMulMonoid, IntAddMonoid


role IntMonoid extednds int
{
public static int Zero = 0;
}

и достаточно всего

Ну или если результат не int добавить функцию приведения добавить в интерфейс

static   implicit operator <R> (T value);


И вызов соотвественно
var c = MatrixMultiply<IntMulMonoid,double>(a, b)
и солнце б утром не вставало, когда бы не было меня
Re[4]: Пример
От: Qbit86 Кипр
Дата: 08.06.20 22:36
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Но практически никто не может представить зачем нужно обобщенное сложение. Такое мало того, что никто не писал, мало кто представляет зачем оно нужно хотя бы в теории.


Но подожди, есть же уже известный пример такого обобщённого алгоритма — Linq Aggregate(). Он внутри использует обобщённую бинарную операцию. Не бином Ньютона!

G>Зато все понимают кост новой фичи языка — лишние ключевые слова и совместимость с существующим, более сложное чтение и навигация по коду. Неожиданные сайд-эффекты от лишнего юзинга.


Полезность новой фичи я не обсуждал. Меня триггернул тезис с позиции wannabe-прагматика: «моноиды ваши заумные не нужны, потому что монады это сложно»

G>Нужен другой пример использования ролей, который будет более понятен всем. Я думаю эта фича, как и куча последних фич, родилась из разработки самого компилятора C#. Могли бы сделать пример с ним, а не с моноидами.


Я бы тоже делал аналогичную презентцию с моноидом по умолчанию — просто мне это кажется самым простым и инструктивным примером (безотносительно видео Мэдса, это частый пример). Но сегодняшний тред пошатнул мою веру в рациональность аудитории. Так что в виду моды на M-фобию можно было заменить IMonoid<T> на IEqualityComparer<T> — всё то же самое, только без страха, что вот-вот сотни вложенных bind/apply заполонят код и сожрут всю память, гроб, отладка, кладбище, монада.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Roles in C# 9. Нужно?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 02:51
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Я вот только не понял зачем IntMulMonoid, IntAddMonoid

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

Про это я писал — это не IMonoid, а IRing.
Плохо то, что в нём мы не можем повторно использовать IntAddMonoid и IntMulMonoid.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 09.06.2020 2:53 Sinclair . Предыдущая версия .
Re[5]: Пример
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 02:54
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Полезность новой фичи я не обсуждал. Меня триггернул тезис с позиции wannabe-прагматика: «моноиды ваши заумные не нужны, потому что монады это сложно»

Вы отвечаете в какой-то не той ветке. Где я писал, что монады это сложно?

Q>Я бы тоже делал аналогичную презентцию с моноидом по умолчанию — просто мне это кажется самым простым и инструктивным примером (безотносительно видео Мэдса, это частый пример). Но сегодняшний тред пошатнул мою веру в рациональность аудитории. Так что в виду моды на M-фобию можно было заменить IMonoid<T> на IEqualityComparer<T> — всё то же самое, только без страха, что вот-вот сотни вложенных bind/apply заполонят код и сожрут всю память, гроб, отладка, кладбище, монада.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Baseline
От: Qbit86 Кипр
Дата: 09.06.20 06:10
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Во-первых, вы что, всерьёз предлагаете чинить проблемы перформанса рантайма при помощи ажно новой фичи языка?


Нет, я просто отвечаю на комментарий
Автор: hi_octane
Дата: 08.06.20
: «Когда дело доходить до оптимизации производительности или потребления памяти — часто проще всю монадную магию выкинуть и переписать с нуля на чистой императивщине.»

S>Если вы уж взялись рассуждать о производительности, за baseline надо брать не моноид, а простой прямолинейный код:


Такой опции попросту нет. У меня алгоритмы складывают не int'ы или double'ы. А TWeight'ы и TDistance'ы.
Глаза у меня добрые, но рубашка — смирительная!
Re[6]: Не та ветка
От: Qbit86 Кипр
Дата: 09.06.20 06:15
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вы отвечаете в какой-то не той ветке.


Нет, ты :)

S>Где я писал, что монады это сложно?


Про тебя речи вообще не идёт :) Я отвечал gandjustas'у, ссылаясь на комментарий hi_octane.
Глаза у меня добрые, но рубашка — смирительная!
Re[4]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.06.20 06:57
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>Я вот только не понял зачем IntMulMonoid, IntAddMonoid

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

S>Про это я писал — это не IMonoid, а IRing.
S>Плохо то, что в нём мы не можем повторно использовать IntAddMonoid и IntMulMonoid.
Ну IMonoid или IRing это просто название интерфейса.
Прелесть в том, что мы можем использовать op перегрузку операторов типа.
И при этом не нужно делать свои специализации в 99% тах случаев



Та же сортировка на перегрузке ==, !=, <, >, <=, >=
Шаблоны C++ прекрасно с ними живут. Правда долго компилируются
и солнце б утром не вставало, когда бы не было меня
Re[9]: Baseline
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 07:20
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Нет, я просто отвечаю на комментарий
Автор: hi_octane
Дата: 08.06.20
: «Когда дело доходить до оптимизации производительности или потребления памяти — часто проще всю монадную магию выкинуть и переписать с нуля на чистой императивщине.»

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

S>>Если вы уж взялись рассуждать о производительности, за baseline надо брать не моноид, а простой прямолинейный код:


Q>Такой опции попросту нет. У меня алгоритмы складывают не int'ы или double'ы. А TWeight'ы и TDistance'ы.

И в чём проблема? Судя по названию — это просто обёртки для соответствующих value-типов, для которых определены те же арифметические операторы, и возможен точно такой же код, как для интов.
Если вас беспокоит производительность, то стремиться надо к тому, чтобы в tight loops бегал обычный нативный код сложения и умножения, а не abstraction penalty с боксингами и косвенными вызовами.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: default(TMonoid)
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 07:31
Оценка:
Здравствуйте, Qbit86, Вы писали:

S>>
var m = new M();

S>>С учётом того, что статические интерфейсы ты ничем не параметризуешь, дефолтного конструктора вполне достаточно.

Q>Даже конструктор не нужен (его констрейнт и вызов), достаточно default(M) с констрейнтом struct. В той статье «Concept C#: Type Classes for the Masses» именно такой подход рассматривался.

Тот же конструктор, вид в профиль — построен на автоматической доступности default constructor для структур.

Q>Всё верно! (Только я в своих API предпочитаю по старинке протаскивать такие policies явно — более гибко. Но действительно, можно и не параметром метода, а параметром дженерика с самостоятельным инстанцированием через default(T).)


S>>С моей точки зрения, для обобщённой операции сложения использовать оператор "+" — баловство.


Q>Категорически согласен.

Ну, вот поэтому я не вижу особой пользы именно от ролей. То, чего я хочу от шейпов/екстеншнов, они не дают. То, что они дают, выглядит не очень-то нужным.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Baseline
От: Qbit86 Кипр
Дата: 09.06.20 07:37
Оценка:
Здравствуйте, Sinclair, Вы писали:

Q>>Такой опции попросту нет. У меня алгоритмы складывают не int'ы или double'ы. А TWeight'ы и TDistance'ы.

S>И в чём проблема? Судя по названию — это просто обёртки для соответствующих value-типов

Нет, это параметеры дженерика. В пользовательском графе веса рёбер могут быть int'ами, decimal'ами, Complex'ами, кастомными типами — чем угодно, заранее неизвестно. Мне нужно абстрагироваться от мономорфных плюсиков.

S>Ну так он же справедлив. В конце концов мы выкидываем не только делегатов, но и интерфейсы, сворачивая цепочку map/reduce в конкретный метод, который вычисляет конкретное замыкание над конкретной коллекцией.


Так ведь нет конкретной коллекции. Речь про написание библиотечного кода, который работает с любыми типами. В момент написания кода ещё не известны типы конечного пользователя. Нет такой альтернативы как «обычный нативный код сложения и умножения».
Глаза у меня добрые, но рубашка — смирительная!
Отредактировано 09.06.2020 7:37 Qbit86 . Предыдущая версия .
Re[5]: Roles in C# 9. Нужно?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 07:40
Оценка:
Здравствуйте, Serginio1, Вы писали:
S>Прелесть в том, что мы можем использовать op перегрузку операторов типа.
Толку-то?
S>И при этом не нужно делать свои специализации в 99% тах случаев
Роли в этом не помогают. В тех самых 99%, роль для IRing<T> будет сводиться к "переопределению" умножения в умножение, а сложения — в сложение. Ну и нафига козе баян?
Я могу переопределить умножение, чтобы в обобщённом коде я мог использовать для умножения умножение? Офигеть как круто.
Единственный осмысленный пример — это возможность переопределить сложение через умножение, чтобы получить перемножение элементов. Ценность — близка к нулю. Если мы дизайним компонент, который пользуется "обобщённым сложением", то совершенно незачем фокусироваться на использовании для него именно инфиксного оператора +, вместо честного (статического) метода Combine().
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[12]: Мономорфизация
От: Qbit86 Кипр
Дата: 09.06.20 08:02
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну вот я ровно об этом вам и говорю — то, чего мы хотим, так это чтобы AddAll(TWeight<double>[] weights) работал не хуже, чем AddAll(double[] weights).


Ещё раз, TWeight — это не обёртка над double, это и есть double. Или int. Или что там ещё у пользователя. Скорее всего это обычный примитивный тип, просто неизвестный заранее. Ровно как тип элементов коллекции в Linq Aggregate(). Может и кастомный MyWeight, это уже как угодно.

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


А чё сразу плохо-то? Произойдёт мономорфизация, исчезнут callvirt'ы, в простых случаях даже инлайнинг будет. Это ж не делегаты с гарантированной косвенностью. Мы ж передаём не IMonoid<T>, а TMonoid where TMonoid : IMonoid<T>.
Глаза у меня добрые, но рубашка — смирительная!
Re[12]: Baseline
От: Qbit86 Кипр
Дата: 09.06.20 08:29
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Если вы уж взялись рассуждать о производительности, за baseline надо брать не моноид, а простой прямолинейный код:

S>...
S>Проблема как раз в том, что рантайм и язык плохо выполняют специализацию и её приходится выполнять вручную.

Добавил вариант для int (вначале повторю вариант для T для сравнения):
internal static T Reduce<T, TMonoid>(this IEnumerable<T> items, TMonoid monoid)
    where TMonoid : IMonoid<T>
{
    if (items is null)
        throw new ArgumentNullException(nameof(items));

    T result = monoid.Identity;
    foreach (T item in items)
        result = monoid.Combine(result, item);

    return result;
}

internal static int ReduceInt32(this IEnumerable<int> items)
{
    if (items is null)
        throw new ArgumentNullException(nameof(items));

    int result = 0;
    foreach (int item in items)
        result += item;

    return result;
}


Методы бенчмарка:
[Benchmark(Baseline = true)]
public int ReduceIntegersBaseline() => s_integers.ReduceInt32();

[Benchmark]
public int ReduceIntegers() => s_integers.Reduce(default(AdditiveInt32Monoid));

...


Результат (правда, на малом числе итераций):
|                 Method |      Mean |     Error |   StdDev | Ratio | RatioSD |
|----------------------- |----------:|----------:|---------:|------:|--------:|
| ReduceIntegersBaseline |  66.76 ns |  4.584 ns | 0.251 ns |  1.00 |    0.00 |
|         ReduceIntegers |  65.82 ns |  8.648 ns | 0.474 ns |  0.99 |    0.01 |
|      AggregateIntegers |  81.74 ns |  2.121 ns | 0.116 ns |  1.22 |    0.00 |
|         ReduceDecimals | 157.94 ns |  8.341 ns | 0.457 ns |  2.37 |    0.02 |
|      AggregateDecimals | 169.61 ns | 20.494 ns | 1.123 ns |  2.54 |    0.02 |
Глаза у меня добрые, но рубашка — смирительная!
Re[13]: Мономорфизация
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 09:05
Оценка:
Здравствуйте, Qbit86, Вы писали:
Q>Ещё раз, TWeight — это не обёртка над double, это и есть double. Или int. Или что там ещё у пользователя. Скорее всего это обычный примитивный тип, просто неизвестный заранее. Ровно как тип элементов коллекции в Linq Aggregate(). Может и кастомный MyWeight, это уже как угодно.
Ну, это хоть совой об пенёк, хоть пнём об сову. Всё равно всё упирается в возможность вызывать встроенные операторы без полиморфизма.

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

Q>А чё сразу плохо-то? Произойдёт мономорфизация, исчезнут callvirt'ы, в простых случаях даже инлайнинг будет. Это ж не делегаты с гарантированной косвенностью. Мы ж передаём не IMonoid<T>, а TMonoid where TMonoid : IMonoid<T>.
Ну, если так-то конечно почему бы и нет.
Можно проверить, получится ли это сделать сейчас — если передать struct Monoid как параметр шаблона, то по идее это должно вызвать принудительную специализацию, и все абстрактные методы будут проинлайнены.

В целом я как раз за extensions в том виде, как их описал Мэдс — я только не понял, будут ли подхватываться существующие методы. Так-то берёшь себе, описываешь Zero и One для десятка встроенных типов — и оппа, вот тебе возможность делать where T: IArithmetic<T> с любым из них. Да и Zero-то описывать надо только для разве что string, потому что нулями всех остальных совершенно случайно являются default(T).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: DisassemblyDiagnoser
От: Qbit86 Кипр
Дата: 09.06.20 09:22
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Всё равно всё упирается в возможность вызывать встроенные операторы без полиморфизма.

Q>>Произойдёт мономорфизация, исчезнут callvirt'ы, в простых случаях даже инлайнинг будет. Это ж не делегаты с гарантированной косвенностью. Мы ж передаём не IMonoid<T>, а TMonoid where TMonoid : IMonoid<T>.
S>Ну, если так-то конечно почему бы и нет.
S>Можно проверить, получится ли это сделать сейчас — если передать struct Monoid как параметр шаблона, то по идее это должно вызвать принудительную специализацию, и все абстрактные методы будут проинлайнены.

Так он уже параметр шаблона. Проверить можно — я подрубил к бенчмарку DisassemblyDiagnoser. (Сам-то я не шарю в CIL; но на вид генерируемый код такой же.)

S>Да и Zero-то описывать надо только для разве что string, потому что нулями всех остальных совершенно случайно являются default(T).


Кроме, например, мультипликативных моноидов, где нейтральным элементом будет единица.
Глаза у меня добрые, но рубашка — смирительная!
Re[6]: Straw man
От: kov_serg Россия  
Дата: 09.06.20 09:28
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>
public interface IGenericAggregationControllerManager<T>
Q>{
Q>    T Identity { get; }
Q>    T Combine(T left, T right);
Q>}


Надо сразу идти дальше
public interface IGenericAggregationControllerManager2<T> : IGenericAggregationControllerManager<T>
{
    T Combine(T[] items);
}
Re[15]: DisassemblyDiagnoser
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 09:32
Оценка:
Здравствуйте, Qbit86, Вы писали:

S>>Можно проверить, получится ли это сделать сейчас — если передать struct Monoid как параметр шаблона, то по идее это должно вызвать принудительную специализацию, и все абстрактные методы будут проинлайнены.

Q>Так он уже параметр шаблона. Проверить можно — я подрубил к бенчмарку DisassemblyDiagnoser. (Сам-то я не шарю в CIL; но на вид генерируемый код такой же.)

S>>Да и Zero-то описывать надо только для разве что string, потому что нулями всех остальных совершенно случайно являются default(T).


Q>Кроме, например, мультипликативных моноидов, где нейтральным элементом будет единица.

А вам обычно мультипликативный моноид и не нужен. Вам обычно нужно умение вызывать встроенный оператор *.
Ну, вот как в примере про умножение матриц — там нафиг не нужна единица, нужно собственно умножение, сложение, и zero, он же default.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.06.20 09:43
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:

S>>Прелесть в том, что мы можем использовать op перегрузку операторов типа.
S>Толку-то?
S>>И при этом не нужно делать свои специализации в 99% тах случаев
S>Роли в этом не помогают. В тех самых 99%, роль для IRing<T> будет сводиться к "переопределению" умножения в умножение, а сложения — в сложение. Ну и нафига козе баян?
S>Я могу переопределить умножение, чтобы в обобщённом коде я мог использовать для умножения умножение? Офигеть как круто.
S>Единственный осмысленный пример — это возможность переопределить сложение через умножение, чтобы получить перемножение элементов. Ценность — близка к нулю. Если мы дизайним компонент, который пользуется "обобщённым сложением", то совершенно незачем фокусироваться на использовании для него именно инфиксного оператора +, вместо честного (статического) метода Combine().

Берем QuikSort и ничего там переопределять вообще не нужно.
И куча алгоритмов для арифметических типов переопределять не надо. Написал для инта проверил, и все остальное для всех алгеброических типов идет.

Суть то ролей в том, что бы ты использовал текущую перегрузку опрераторов. Не хочешь действуй как и раньше только вместо
Equality в role сделай перегрузку == и !=
Обычно для каждого типа они есть.

В твоем примере можно использовать текущие перегрузки операторов.
и солнце б утром не вставало, когда бы не было меня
Re[16]: Замкнутая бинарная операция
От: Qbit86 Кипр
Дата: 09.06.20 09:52
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А вам обычно мультипликативный моноид и не нужен. Вам обычно нужно умение вызывать встроенный оператор *.


Всё верно, для многих алгоритмов нужна не абстракция «моноид», а только абстракция «замкнутая бинарная операция».

S>Ну, вот как в примере про умножение матриц — там нафиг не нужна единица, нужно собственно умножение, сложение, и zero, он же default.


Так вроде и zero не нужен? Когда у тебя коллекции непустые (здесь коллекции это строки и столбцы матрицы), то начальный элемент задавать не нужно (reduce вместо fold).
Глаза у меня добрые, но рубашка — смирительная!
Re[9]: IEqualityComparer<T>
От: hi_octane Беларусь  
Дата: 09.06.20 09:56
Оценка:
Q>А я объяснил, почему твоё объяснение некорректное. Про straw man не только к AlexRK относилось, но и к тебе в первую очередь; почитай, пожалуйста.
Корректное. Просто тебе не нравится сама мысль о том что кто-то может прекрасно знать M-words и при этом весьма скептически на них смотреть. Я в первом же утверждении написал "людям сначала нравится элегантность и единообразность монад, моноидов и этого всего". И продолжаю придерживаться этого тезиса. Для меня эти конструкции одинаково "заражают" код. Решают местечковую проблемку, но порождают другую, которая на дистанции только растёт.

_>>Сейчас ты решил что всё понял.

Q>Да, я всё понял: ты слышал звон, но не знаешь, где он. M-word — эта сложна! Нипанятна!
Ну вот пруф. Ты решил что мне именно "сложна и нипанятна". Представить что кому-то кроме достоинств понятны и недостатки — пока никак.

Q>А, речь не про этот M-word?.. Всё равно от лукавого, буквы-то те же! Нафиг не нужон моноид ваш!

Это то что ты понял. А то что там было написано: "эти абстракции порождают похожие проблемы в большом проекте".

Q>>>Внезапно, код с моноидом в стиле как на видео будет быстрее, чем стандартная лапша с делегатами : )

Q>Хоть по этому вопросу возражений нет.
Ты пропустил замечание что для сложения и умножения чаще всего вообще не стоит лепить лапшу с делегатами.

Q>Если при использовании IMonoid<T> непременно такие сложности возникнут, то они же неизбежно должны возникнуть и при использовании интерфейса IEqualityComparer<T>? Сценарии использования у них ведь строго одинаковые.

Разные. Компаратор очень ограничен в возможностях. Предел комбинирования для компараторов — библиотечка OrderBy...ThenBy, и всё. Новые экземпляры T компаратор не порождает. Моноиды гибче и в терминальной стадии распространения могут заменять собой не то что IEqualityComparer, а даже просто IComparer.
Re[10]: IEnumerable<T>
От: Qbit86 Кипр
Дата: 09.06.20 10:11
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>Ты пропустил замечание что для сложения и умножения чаще всего вообще не стоит лепить лапшу с делегатами.


Хорошо. Что тогда нужно лепить вместо моноида в этом алгоритме? Как его переписать без еретического моноида?
internal static T Reduce<T, TMonoid>(this IEnumerable<T> items, TMonoid monoid)
    where TMonoid : IMonoid<T>
{
    if (items is null)
        throw new ArgumentNullException(nameof(items));

    T result = monoid.Identity;
    foreach (T item in items)
        result = monoid.Combine(result, item);

    return result;
}


Q>>Если при использовании IMonoid<T> непременно такие сложности возникнут, то они же неизбежно должны возникнуть и при использовании интерфейса IEqualityComparer<T>? Сценарии использования у них ведь строго одинаковые.

_>Разные. Компаратор очень ограничен в возможностях.
_>Новые экземпляры T компаратор не порождает.

Хорошо а IEnumerable<T> — это тоже харам? Он же порождает; ты его тоже избегаешь? Потому что на энумераторах можно много чего наворотить.
Глаза у меня добрые, но рубашка — смирительная!
Re[7]: Roles in C# 9. Нужно?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 15:28
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Sinclair, Вы писали:


S>Берем QuikSort и ничего там переопределять вообще не нужно.

Да, только в C# не работает.
S>И куча алгоритмов для арифметических типов переопределять не надо. Написал для инта проверил, и все остальное для всех алгеброических типов идет.
Коллега, вы сецчас пишете о чём-то своём.
S>Суть то ролей в том, что бы ты использовал текущую перегрузку опрераторов. Не хочешь действуй как и раньше только вместо
Ничего подобного в ролях нету.
S> В твоем примере можно использовать текущие перегрузки операторов.
Нельзя.
Мэдс же показал код — чтобы обобщённый код подхватил операторы типа +, нужно, чтобы либо генерик-параметр реализовал IMonoid<T> (ну, или там IRing<T>), либо руками выписывать роль, в которой IMonoid<int>.operator+ мапится на int.operator+.
Чтобы этот же код заработал не с int, а с Complex, надо опять руками выписывать роль ComplexAddMonoid и руками же скармливать её в генерик-параметр метода.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Замкнутая бинарная операция
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.06.20 15:33
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Всё верно, для многих алгоритмов нужна не абстракция «моноид», а только абстракция «замкнутая бинарная операция».

Ещё чаще нам нужно просто вызвать у типа operator +, а сделать это невозможно потому, что язык вплоть до нынешней версии устроен так, что это невозможно.
Трюк с instance monoid вы применяете не потому, что вам реально нужна возможность менять тип замкнутой операциии на лету, а чтобы обойти вот это ограничение, которое в С++ работает благодаря compile-time специализации.
То есть руками объясняете обобщённому коду, что плюс — это плюс, и он порождает почти такой же бинарь, как настоящий специализированный код.

S>>Ну, вот как в примере про умножение матриц — там нафиг не нужна единица, нужно собственно умножение, сложение, и zero, он же default.


Q>Так вроде и zero не нужен? Когда у тебя коллекции непустые (здесь коллекции это строки и столбцы матрицы), то начальный элемент задавать не нужно (reduce вместо fold).

Технически — да. С т.з. простоты кода удобнее писать с default/zero.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 09.06.20 16:09
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>Здравствуйте, Sinclair, Вы писали:


S>>Берем QuikSort и ничего там переопределять вообще не нужно.

S>Да, только в C# не работает.
Ну так роли для этого и сделаны.
S>>И куча алгоритмов для арифметических типов переопределять не надо. Написал для инта проверил, и все остальное для всех алгеброических типов идет.
S>Коллега, вы сецчас пишете о чём-то своём.
S>>Суть то ролей в том, что бы ты использовал текущую перегрузку опрераторов. Не хочешь действуй как и раньше только вместо
S>Ничего подобного в ролях нету.
S>> В твоем примере можно использовать текущие перегрузки операторов.
S>Нельзя.
S>Мэдс же показал код — чтобы обобщённый код подхватил операторы типа +, нужно, чтобы либо генерик-параметр реализовал IMonoid<T> (ну, или там IRing<T>), либо руками выписывать роль, в которой IMonoid<int>.operator+ мапится на int.operator+.
S>Чтобы этот же код заработал не с int, а с Complex, надо опять руками выписывать роль ComplexAddMonoid и руками же скармливать её в генерик-параметр метода.
Там

Смотрим старый вброс на шейпах

https://www.c-sharpcorner.com/article/candidate-features-for-c-sharp-9/

Original Source


public shape SGroup<T>      
{      
 static T operator +(T t1, T t2);      
 static T Zero {get;}       
}

This declaration says that a type can be an SGroup<T> if it implements a+ operator over T, and a Zero static property.



public extension IntGroup of int: SGroup<int>    
{    
 public static int Zero => 0;    
}

And the extension.



public static AddAll<T>(T[] ts) where T: SGroup<T> // shape used as constraint    
{    
 var result = T.Zero; // Making use of the shape's Zero property    
 foreach (var t in ts) { result += t; } // Making use of the shape's + operator    
 return result;    
}

Let us call the AddAll method with some ints,


int[] numbers = { 5, 1, 9, 2, 3, 10, 8, 4, 7, 6 };     
  
WriteLine(AddAll(numbers)); // infers T = int


Я так понял, что вместо
 public shape SGroup<T>


можно использовать интерфейс
а вместо
public extension IntGroup of int: SGroup<int>


можно использовать

role IntGroup  extednds int


И в примере Мэдс все тоже самое. Ты переопределяешь только
public static int Zero => 0;



И разница в том, что int[] нужно привести к IntGroup[]

В любом случае определить можно сделать один интерфейс для всех перегрузок операторов алгеброических типов и прописать для них роли
и использовать эти роли хоть откуда как Func<> Action итд.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 09.06.2020 16:23 Serginio1 . Предыдущая версия . Еще …
Отредактировано 09.06.2020 16:15 Serginio1 . Предыдущая версия .
Re[18]: Constraints
От: Qbit86 Кипр
Дата: 09.06.20 23:11
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ещё чаще нам нужно просто вызвать у типа operator +, а сделать это невозможно потому, что язык вплоть до нынешней версии устроен так, что это невозможно.


В C++ можно вызвать «operator +», да. А ещё позвать оператор %, оператор запятую и вообще любую дичь а-ля Perl, которую только сможет натопать по клавиатуре кот — компилятор-то шаблоны не проверяет. Нет constraint'ов как в C# и других языках здорового человека.
Глаза у меня добрые, но рубашка — смирительная!
Re[7]: default(TMonoid)
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.06.20 02:43
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Интуитивно кажется, что это не так, и «default(TMonoid)» должен быть эффективнее, чем «new TMonoid()» для создания экземпляров value-типов вроде decimal в generic-коде.

Мы говорим об одной операции на вызов. Это дорого только в каких-то сценариях типа динамической композиции.
Если мы про массовую обработку, то можно себе позволить вообще на лету компилировать специализированный код всего цикла.
Вы больше теряете из-за того, что используете != вместо < при обходе массивов

Q>Но точно не знаю, так что побенчмаркал. В .NET Core < 3.0 действительно new TMonoid() заметно медленнее. Но в .NET Core 3.0+ уже разницы нет. Activator.CreateInstance() по-прежнему есть в IL, но, видимо, JIT как-то лучше оптимизирует.

Да, можно глянуть в финальный x86 — для тривиальных конструкторов там скорее всего не сильно хуже, чем тупо инкремент указателя кучи.
Но вы, конечно же, правы: если мы жмём такты, то interface over struct as generic parameter — то, что доктор прописал.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Roles in C# 9. Нужно?
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.06.20 02:47
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Смотрим старый вброс на шейпах


S>https://www.c-sharpcorner.com/article/candidate-features-for-c-sharp-9/


S>Original Source



S>
S>public shape SGroup<T>      
S>{      
S> static T operator +(T t1, T t2);      
S> static T Zero {get;}       
S>}  
S>

S>

S>This declaration says that a type can be an SGroup<T> if it implements a+ operator over T, and a Zero static property.



S>
S>public extension IntGroup of int: SGroup<int>    
S>{    
S> public static int Zero => 0;    
S>}   
S>

S>

S>And the extension.



S>
S>public static AddAll<T>(T[] ts) where T: SGroup<T> // shape used as constraint    
S>{    
S> var result = T.Zero; // Making use of the shape's Zero property    
S> foreach (var t in ts) { result += t; } // Making use of the shape's + operator    
S> return result;    
S>}   
S>

S>Let us call the AddAll method with some ints,


S>
S>int[] numbers = { 5, 1, 9, 2, 3, 10, 8, 4, 7, 6 };     
  
S>WriteLine(AddAll(numbers)); // infers T = int   
S>


S> Я так понял, что вместо

S>
S> public shape SGroup<T>
S>


S>можно использовать интерфейс

S> а вместо
S>
S>public extension IntGroup of int: SGroup<int> 
S>


S>можно использовать


S>
S>role IntGroup  extednds int
S>


S> И в примере Мэдс все тоже самое. Ты переопределяешь только

S>
S>public static int Zero => 0; 
S>

Да, точно, операторы таки подхватываются.

S>И разница в том, что int[] нужно привести к IntGroup[]

Вот именно. extension в этом смысле лучше, чем роли, т.к. работает автоматика приведения.

S>В любом случае определить можно сделать один интерфейс для всех перегрузок операторов алгеброических типов и прописать для них роли

S>и использовать эти роли хоть откуда как Func<> Action итд.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[19]: Constraints
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.06.20 02:53
Оценка:
Здравствуйте, Qbit86, Вы писали:
Q>В C++ можно вызвать «operator +», да. А ещё позвать оператор %, оператор запятую и вообще любую дичь а-ля Perl, которую только сможет натопать по клавиатуре кот — компилятор-то шаблоны не проверяет. Нет constraint'ов как в C# и других языках здорового человека.
Об этом и речь. Собственно, с моей точки зрения нужно продолжать фокусироваться над проблемой "выразить наличие статических мемберов типа в ограничениях дженериков", и сопутствующей ей проблемой "как теперь сделать так, чтобы встроенные типы удовлетворяли этим ограничениям без переделки CLR".
extensions выглядят вполне вменяемым решением этой проблемы — я могу скомбинировать свой обобщённый алгоритм с набором екстеншнов для всех встроенных типов, и пользователи смогут им пользоваться без единой лишней строчки кода.
Роли выглядят решением какой-то другой проблемы. Вот ваш Reduce со struct моноидом уже вполне себе прекрасен. Введение ролей его никак не улучшит ни со стороны пользователя, ни со стороны автора — ну сможете вы заменить Combine на += — код от этого не станет более читаемым.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: mov
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.06.20 02:57
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>А ты пытаешься навязать мне какую-то другую задачу. Хочешь итерироваться по массиву? Окей, тогда сначала материализуй исходный IEnumerable<T> в массив, и ходи по нему. Например, рёбра остовного дерева не в массиве приходят, а генерируются в процессе обхода графа.


Q>Но даже если массивы. В обобщённом коде отстать от мономорфного всего на одну инструкцию промежуточного копирования — отличный результат, уже на порядок (десятичный) лучше протаскивания делегата в аналогичный Linq-метод.



Q>Окей, даже хоть и не сказывается, заменим на foreach, как в твоём варианте.


S>>В-третьих, удивительно, но факт: JIT всё же порождает разный код для generic случая и для специализации. Если починить проблему с !=, то generic версия начинает работать вот так:

S>>
S>>| Special                                  | Generic  
S>>+------------------------------------------+-------------------------------------------
S>>| 7FF85A3A41A7 movsxd    r9,edx            | 7FF85A3B3ED7 movsxd    r9,edx
S>>|                                          | 7FF85A3B3EDA mov       r9d,[rcx+r9*4+10]
S>>| 7FF85A3A41AA add       eax,[rcx+r9*4+10] | 7FF85A3B3EDF add       eax,r9d
S>>| 7FF85A3A41AF inc       edx               | 7FF85A3B3EE2 inc       edx
S>>


Q>У меня разницы нет; эта промежуточная инструкция есть в обоих вариантах 🤷 Такое окружение:

Q>
.NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT

Причуды джита

// * Summary * BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.836 (1909/November2018Update/19H2) Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores .NET Core SDK=3.1.300 [Host] : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT Job-SGTOAB : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT


Q>У меня на RunMode.Long такой результат:

Q>
Q>|                 Method |       Mean |     Error |    StdDev |     Median | Ratio | RatioSD | Code Size |
Q>|----------------------- |-----------:|----------:|----------:|-----------:|------:|--------:|----------:|
Q>| ReduceIntegersBaseline |   4.879 us | 0.0074 us | 0.0385 us |   4.879 us |  1.00 |    0.00 |     131 B |
Q>|         ReduceIntegers |   4.851 us | 0.0121 us | 0.0621 us |   4.876 us |  0.99 |    0.02 |     151 B |
Q>|      AggregateIntegers |  74.457 us | 0.1019 us | 0.5166 us |  74.364 us | 15.26 |    0.15 |     419 B |
Q>

Ну что ж, поздравляю с отличным результатом!
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Оптимизация производительности
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.06.20 03:03
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Будте добры, а то не верится что будет быстрее. Уж больно T Reduce<T, TMonoid>(this IEnumerable<T> items, TMonoid monoid) абстрактный, со всеми косвенностями.

В управляемом мире ничего абстрактного нет. Удобнее, конечно, начинать не с TMonoid, а с Expression<Func<T, T, T>>, но в принципе ничего военного нет и при работе через интерфейс — вынимаем из него MSIL, и проверяем несколько типовых случаев. Если выполнены сразу все условия — items представляет собой T[]/Span<T>/ReadonlySpan<T>, и код Combine попадает в один из десятка паттернов — то мы переключаемся на SIMD. А если нет — то делаем fallback на обычный код, обработанный JIT-ом — как показал коллега QBit86, он совпадает по быстродействию с ручным кодом итерирования.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Классы типов для бедных
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 10.06.20 12:16
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, varenikAA, Вы писали:


AA>>Roles in C# 9. Нужно?


Q>Признаться, я не очень понял, чем эти «роли» отличаются от того, что раньше питчилось под названием «шейпы», кроме синтаксиса. И не нахожу их особенно полезными.

Все очень просто. В большей степени это нужно для алгеброических типов.
C++ шаблоны используют перегрузку методов напрополую. Правда там кодогенерация.
Для ролей же можно генерировать инлайн код из дженерика при Jit е

Не нужно генерировать для каждого типа реализацию интерфейса перегруженных операторов итд.
Из примеров наверное можно вспомнить System.Numerics. Там уже перегрузка операторов есть
Да и для большинства числовых дженериков подойдет.

Думаю в большинсте случаев это нужно для скорости, но и берешь кучу алгоритмов и используешь операторы, вместо интерфейсов, делегатов.
Удобно!
и солнце б утром не вставало, когда бы не было меня
Отредактировано 10.06.2020 17:44 Serginio1 . Предыдущая версия .
Re[11]: IEnumerable<T>
От: hi_octane Беларусь  
Дата: 10.06.20 14:31
Оценка:
_>>Ты пропустил замечание что для сложения и умножения чаще всего вообще не стоит лепить лапшу с делегатами.
Q>Хорошо. Что тогда нужно лепить вместо моноида в этом алгоритме? Как его переписать без еретического моноида?
Алгоритма-то нету — сумматор на моноиде и всё. Если ничего сложнее в коде нет, то и не надо переписывать. Задуматься что "свернули не туда" надо если потребитель твоего кода вынужден описывать и передавать пяток моноидов для разных целей, или если они у тебя или у пользователя поштучно и группами кочуют из метода в метод.

Q>Хорошо а IEnumerable<T> — это тоже харам? Он же порождает; ты его тоже избегаешь? Потому что на энумераторах можно много чего наворотить.

IEnumerable уже часть языка, избегать не обязательно. Но с шаблоном "тут последовательность данных, значит IEnumerable" закопать проект вполне можно.
Re[3]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.06.20 07:09
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>По мне так данная реализация убога. Нужно делать как в Go, т.е. чтобы вместо обычного интерфейса можно было был передать любой объект у которого есть все члены этого интерфейса (совпадающие по имени + сигнатура). Тогда этот костыль будет не нужен.


С точки зрения перегрузки методов так и есть берутся текущие, но ты можешь переопределить в ролях.
Если не интерфейс, то что будет выступать контрактом?
и солнце б утром не вставало, когда бы не было меня
Отредактировано 17.06.2020 10:19 VladD2 . Предыдущая версия .
Re[4]: Roles in C# 9. Нужно?
От: Jack128  
Дата: 17.06.20 09:07
Оценка:
Здравствуйте, Serginio1, Вы писали:

VD>>По мне так данная реализация убога. Нужно делать как в Go, т.е. чтобы вместо обычного интерфейса можно было был передать любой объект у которого есть все члены этого интерфейса (совпадающие по имени + сигнатура). Тогда этот костыль будет не нужен.

S> С точки зрения перегрузки методов так и есть берутся текущие, но ты можешь переопределить в ролях.
S>Если не интерфейс, то что будет выступать контрактом?
Дык интерфейс и будет. Влад, как я понял, хочет структурную типизацию.
ИМХО — нафиг. В C#9 реализовать интерфейс (при наличии в типе всех членов интерфейса) — можно одной строчкой. extention MyClassExt intends MyClass: IMyIntf {} см тут
Re[5]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.06.20 09:49
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Здравствуйте, Serginio1, Вы писали:


VD>>>По мне так данная реализация убога. Нужно делать как в Go, т.е. чтобы вместо обычного интерфейса можно было был передать любой объект у которого есть все члены этого интерфейса (совпадающие по имени + сигнатура). Тогда этот костыль будет не нужен.

S>> С точки зрения перегрузки методов так и есть берутся текущие, но ты можешь переопределить в ролях.
S>>Если не интерфейс, то что будет выступать контрактом?
J>Дык интерфейс и будет. Влад, как я понял, хочет структурную типизацию.
J>ИМХО — нафиг. В C#9 реализовать интерфейс (при наличии в типе всех членов интерфейса) — можно одной строчкой. extention MyClassExt intends MyClass: IMyIntf {} см тут

Дык и чем role отличается от extention ?
Для роле нужно указать тип, для extention он выводится
и солнце б утром не вставало, когда бы не было меня
Re[4]: Roles in C# 9. Нужно?
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.06.20 10:47
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>С точки зрения перегрузки методов так и есть берутся текущие, но ты можешь переопределить в ролях.

S>Если не интерфейс, то что будет выступать контрактом?

Ты, похоже, ничего из написанного мной не понял. Посмотри как в Go реализованы интерфейсы. Там какие-то роли не нужны. Любой объект обладающий набором членов совпадающим с набором членов интерфейса считается реализующим этот интерфейс (утиная типизация). Есть у тебя интерфейс:
interface IFoo
{
  void Bar();
}


И есть объект:
class Baz
{
  void X() {}
  void Bar() {}
}

Объект типа Baz можно передать туда где требуется IFoo, так как Baz реализует все члены IFoo (в данном случае Bar).

Все! Никакие роли не нужны! Если надо добавить реализацию недостающих методов можно пользоваться наследованием. Ну, разве что для запечатанных типов роли нужны для обхода запечатанности.

А тут нужно каждый раз придется писать кучу кода руками. Конечно, лучше чем ничего. Но по сравнению с Go — хуже.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Roles in C# 9. Нужно?
От: Jack128  
Дата: 17.06.20 10:51
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Jack128, Вы писали:


J>>Здравствуйте, Serginio1, Вы писали:


VD>>>>По мне так данная реализация убога. Нужно делать как в Go, т.е. чтобы вместо обычного интерфейса можно было был передать любой объект у которого есть все члены этого интерфейса (совпадающие по имени + сигнатура). Тогда этот костыль будет не нужен.

S>>> С точки зрения перегрузки методов так и есть берутся текущие, но ты можешь переопределить в ролях.
S>>>Если не интерфейс, то что будет выступать контрактом?
J>>Дык интерфейс и будет. Влад, как я понял, хочет структурную типизацию.
J>>ИМХО — нафиг. В C#9 реализовать интерфейс (при наличии в типе всех членов интерфейса) — можно одной строчкой. extention MyClassExt intends MyClass: IMyIntf {} см тут

S> Дык и чем role отличается от extention ?

S> Для роле нужно указать тип, для extention он выводится

Ну вот как раз фича внешней реализации интерфейсов в новых экстеншенах сильно пересекается с областью использования ролей, ИМХО нужно оставлять что то одно.
Re[5]: Roles in C# 9. Нужно?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.06.20 10:57
Оценка:
Здравствуйте, VladD2, Вы писали:



VD>А тут нужно каждый раз придется писать кучу кода руками. Конечно, лучше чем ничего. Но по сравнению с Go — хуже.

Так для роли тоже самое просто прписал один раз и используй.

А если тебе захотелось переопределить какой то оператор то роли это позволяют. Например сравнение строк без учета регистров
А как с этим в GO?
и солнце б утром не вставало, когда бы не было меня
Отредактировано 17.06.2020 18:12 Serginio1 . Предыдущая версия . Еще …
Отредактировано 17.06.2020 11:01 Serginio1 . Предыдущая версия .
Re[5]: Roles in C# 9. Нужно?
От: Ночной Смотрящий Россия  
Дата: 17.06.20 11:19
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Объект типа Baz можно передать туда где требуется IFoo, так как Baz реализует все члены IFoo (в данном случае Bar).


Это сломает всю совместимость CLR, так что увы, но не в дотнете.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[6]: Roles in C# 9. Нужно?
От: Sharov Россия  
Дата: 17.06.20 17:06
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>Это сломает всю совместимость CLR, так что увы, но не в дотнете.


А может для подобного DLR использовать?
Кодом людям нужно помогать!
Re[7]: Roles in C# 9. Нужно?
От: Ночной Смотрящий Россия  
Дата: 17.06.20 17:39
Оценка:
Здравствуйте, Sharov, Вы писали:

S>А может для подобного DLR использовать?


А в DLR совместимых языках в основном структурная типизация в том или ином виде есть.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[8]: Roles in C# 9. Нужно?
От: Sharov Россия  
Дата: 17.06.20 18:34
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>А в DLR совместимых языках в основном структурная типизация в том или ином виде есть.


И у чем тогда проблема подтолкнуть утиную типизацию для интерфейсов в шарп?
Кодом людям нужно помогать!
Re[5]: Roles in C# 9. Нужно?
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.06.20 04:13
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Все! Никакие роли не нужны! Если надо добавить реализацию недостающих методов можно пользоваться наследованием. Ну, разве что для запечатанных типов роли нужны для обхода запечатанности.
Тут есть пара нюансов.
1. Нужны не только экземплярные, но и статические методы. Например, operator +, он же op_Addition.
2. В классах типа System.Int32 нужных нам статических методов нет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Roles in C# 9. Нужно?
От: Ночной Смотрящий Россия  
Дата: 18.06.20 07:58
Оценка:
Здравствуйте, Sharov, Вы писали:

S>И у чем тогда проблема подтолкнуть утиную типизацию для интерфейсов в шарп?


В том что шарп это не динамика.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[7]: Оптимизация производительности
От: varenikAA  
Дата: 22.07.21 09:29
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>
Q>[Benchmark(Baseline = true)]
Q>public int ReduceIntegers() => s_integers.Reduce(default(AdditiveInt32Monoid));

Q>[Benchmark]
Q>public int AggregateIntegers() => s_integers.Aggregate(0, (left, right) => left + right);

Q>


Стало интересно
        [Benchmark]
        public int SumIntegers() {
        int sum = 0;
        foreach(int i in s_integers) sum += i;
            return sum;
    }


представил как я смотрю на эти методы.
Да, без погружения в ФП их сразу не поймешь, да и в скорости в 10 раз проигрывают.
☭ ✊ В мире нет ничего, кроме движущейся материи.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.