Pull to refresh

Comments 31

Можно было бы упомянуть что ваши IMonoid<T> это классы типов. В языках типа Haskell или Rust (там они называются трейтами, но суть почти та же) они являются одним из основных способов абстракции, да и в других языках типа Scala играют большую роль.

Спасибо за комментарий! Он помог доработать статью :)

С учётом того, что Робин Милнер предложил язык ML до моего рождения, мы сильно тормозим с математическими веяниями в программировании. :-(

Мне кажется, что абстрактная алгебра для программирования не стала каким-то откровением и сейчас просто занимает свое место среди множества других шаблонов проектирования. Примерно как абстрактная живопись стреди всего остального изобразительного искусства.

ML имеет слишком жёсткие ограничения. Тайпклассы позволяют выйти за эти рамки.
Так что веяния - это к хаскеллу там, к идрису. Они вовсю развиваются своими путями.

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

Даже в статье пример: давайте посчитаем среднее в группе (R*Z, +, (0,0)). Формально, свели задачу к известной. А на деле, посчитали длину массива с помощью суммирования списка единичек!

Когда у тебя в коде нет ничего, кроме математики, - наверно, можно там жестоко поупрощать. Как это делает компилятор хаскелла, заточенный на такие упрощения. А насколько глубоко это получится для шарпа со вкраплениями?

Мы можем с помощью него например сгруппировать строки по их числу вхождений или найти слова одинаковой длины.

для группировки я применю LINQ

var d1 = strings.GroupBy(s => s).ToDictionary(g => g.Key, g => g.Count());

var d1 = strings.GroupBy(s => s.Length).ToDictionary(g => g.Key, g => g.ToList());

Или что-нибудь, что вы придумаете

честно пытался придумать что-нибудь неподвластное LINQ или перегрузке операторов, но увы

public class AveragedValue {

public AveragedValue() : this(0, 0) { }

public double Get() => _sum / _count;

}

public class Avg : IMonoid<AveragedValue> {

public AveragedValue Zero => new ();

}

Ничего, что вызвав Avg.Zero().Get() получим 0/0?

Спасибо, не подумал об этом, поправил)

А зря поправили. Среднее среди пустого множества неопределено, а вовсе не равно 0.

Замечательно, спасибо.

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

Пожалуйста, пишите ещё или, может, поделитесь литературой о том, как алгебру можно применять при программировании.

Почти всё современное ФП построено на применении законов алгебры и некоторых упрощенных конструкций из теории категорий. Если вам интересны эти темы, можете начать с книги Бартоша Милевского «Category theory for programmers», она написана с примерами на Haskell, но есть переводы на Scala и C# + F#. Примеров на пайтоне, увы, мне быстро найти не удалось — возможно, вы сможете.


Также очень рекомендую посмотреть доклады конференций вроде Strange Loop или Scala Love. Из второй очень советую глянуть доклад Луки Якобовица «Monoids, monoids, monoids», он хорошо дополняет текущую статью.

На Ютубе есть ещё курс лекций Category Theory For Beginners, Ричарда Саусвелла с его чудесным акцентом. А ещё по теоркату есть хорошее введение в книге Голдблатт Р. Топосы. Категорный анализ логики.

Спасибо! Уже готовлю материал :)

Мы задействовали только определения, а какой прорыв случится, когда в ход пойдут теоремы, боюсь представить

А вы не бойтесь :) Посмотрите на языки proof assistant'ы Coq, Agda, Lean. Для последнего есть шикарнейшая библиотека с формализацией математики: https://leanprover-community.github.io/index.html. Если вас интересует эта тема, поищите статьи и книги на тему формальной верификации (например, Verified Functional Programming in Agda написана просто отлично).

Спасибо большое, обязательно посмотрю!

Благодарю за информацию про L∃∀N. Это действительно мощно.

Беда в том, что авторская номенклатура идей из этой книги "не взлетела". Взлетели их однозначные соответствия из FP и DDD, да и из OOP.

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

Кто боится когнитивных расходов, тому путь в OOP. Серьезно, как они потом будут читать код?

var richest = people.Sum<Person, Max<Person>>();

Магия!

Это просто артефакт неподходящего синтаксиса. :-( Если взять синтаксис ISWIM (ML или Haskell), то будет сильно проще.

Я к тому, что код делает не то, как это написано: он считает не сумму людей, а ищет самого богатого.

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

Использование каких-либо технических приемуществ языка не отменяет необходимости в том, чтобы писать на нём по-человечески. Например, одна из претензий к Хаскелистам, что в сообществе слишком часто используются однобуквенные сокращениям, в результате код становится непонятным несмотря на простой синтаксис и семантику языка (без расширений язык значительно проще той же Java, а семантически - Питона).

Увы, "что один человек сделал, другой всегда поломать сможет".

Статья 19 Конституции РФ

public class Person : IComparable<Person>
{
    public int CompareTo(Person other) => 0;
}

В языках не хватает самого интересного - требования к операциям.

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

Наконец, можно не вводить новый интерфейс моноид (или даже группа), а проверять полугруппу на соответствие условий.

На самом деле, устроить проверку ассоциативности, коммутативности и других свойств можно разными способами. Например, через перехват методов инструментами аспектно-ориентированного программирования (PostSharp, Castle). Или через реализацию какой-нибудь такой истории.

public abstract class SemiGroup<T>
{
    protected abstract T PlusInternal(T left, T right);

    public T Plus(T left, T right)
    {
        AssertAssociative(PlusInternal); 
        ...
        return PlusInternal(left, right);
    }
}

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

Полезно всё-таки сформулировать инварианты и написать Property-Based тесты.

Обращу внимание на тот нюанс, что применение математики имеет смысл в рамках обработки данных, которые являются частным случаем обработки информации. Всё снова упирается в то, что человеку, для грамотного применения алгебры, необходимо знать как минимум фундамент информатики(ака. Наука об информации, ака Computer Science), к этому надо знать матан, к этому знать алгебру(ака наука о данных, ака Data Science), но увы, сейчас спрос идёт на разработчиков мобильных приложулек, где конструкция вида: if-else if-else if-else if-else это норма. Что произошло ввиду развития подхода: да кому нужна эта оптимизация? У нас компы такие быстрые, что 1мс разницы в алгоритме никто не увидит. Вот мы и получили. Толпу кодеров, перекидывающихся модными питоновскими библиотеками и совсем ничего не понимающими в базовой математике и основе основ.

Базовая математика и её основы это специфческие темы, которые становятся более менее понятны на последних курсах профильных университетов и дальше. Разработка ПО это прикладная дисциплина, как физика, которая развивается параллельным курсом, периодически таская из математики интересные идеи. Но цели разные. Математика борется с избыточностью, а программирование - со сложностью (причем не только алгоритмической, но и сложностью в обучении, использовании, эксплуатации, поиске бюджета, подходящего персонала и т.п. все и сразу). Условно, если разобраться в формуле может только человек с несколькими высшими образованиями, то это крутая формула. Если аналогичные скиллы нужны, чтобы разобраться в коде, то это плохое решение.

Sign up to leave a comment.

Articles