Зачем нужны делегаты в C#?

    image
    Продумывая архитектуру очередного класса вы понимаете, что вам очень бы пригодилась возможность передать в качестве аргумента кусок исполняемого кода. Это позволило бы вам избежать вереницы if-ов и case-ов и сделало бы ваш код более элегантным Девушки восхищенно бы охали и непременно оставляли бы вам свой телефончик в комментах. Кхм… что-то я увлекся.

    Итак как это делается в C#? Например вы пишете калькулятор и у вас есть простейшая логика:
    public double PerformOperation(string op, double x, double y)
    {
    	switch (op)
    	{
    		case "+": return x + y;
    		case "-": return x - y;
    		case "*": return x * y;
    		case "/": return x / y;
    		default: throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
    	}
    }
    


    Это простое и изящное решение имеет право на жизнь, но у него есть некоторые проблемы:
    • Софт изменчив. Завтра вам понадобится добавить взятие по модулю и тогда придется перекомпилировать класс. На определенных стадиях проекта это недешевое удовольствие для потребителей вашего класса.
    • Код в текущем виде не имеет никаких проверок входных данных. Если их добавить, то switch неприлично разрастется.



    Как говорят мои израильские друзья ма лаасот?

    Во первых надо инкапсулировать код в функции:
    switch (op)
    {
    	case "+": return this.DoAddition(x, y);
    	case "-": return this.DoSubtraction(x, y);
    	case "*": return this.DoMultiplication(x, y);
    	case "/": return this.DoDivision(x, y);
    	default: throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
    }
    ...
    private double DoDivision(double x, double y) { return x / y; }
    private double DoMultiplication(double x, double y) { return x * y; }
    private double DoSubtraction(double x, double y) { return x - y; }
    private double DoAddition(double x, double y) { return x + y; }
    

    Во вторых надо вообще избавиться от свитча:
    private delegate double OperationDelegate(double x, double y);
    private Dictionary<string, OperationDelegate> _operations;
    
    public Calculator()
    {
    	_operations =
    		new Dictionary<string, OperationDelegate>
    		{
    			{ "+", this.DoAddition },
    			{ "-", this.DoSubtraction },
    			{ "*", this.DoMultiplication },
    			{ "/", this.DoDivision },
    		};
    }
    
    public double PerformOperation(string op, double x, double y)
    {
    	if (!_operations.ContainsKey(op))
    		throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
    	return _operations[op](x, y);
    }
    


    Что мы сделали? Мы вынесли определение операций из кода в данные — из свитча в словарь.
    private delegate double OperationDelegate(double x, double y);
    private Dictionary<string, OperationDelegate> _operations;
    


    Делегат это обьект указывающий на функцию. Вызывая делегат, мы вызываем функцию на которую он указывает. В данном случае мы создаем делегат на функцию принимающую два double параметра и возращающую double. Во второй строке мы создаем маппинг между символом операции (+-*/) и её функцией.
    Таким образом мы разрешили первый недостаток: список операций можно изменять по своему усмотрению.

    К сожалению мы поимели лишний делегат, да и запись вида
    { "+", this.DoAddition }
    не так понятна как
    case "+": return x + y;

    Начиная с C# 2.0 мы можем разрулить эту проблему внедрением анонимных методов:
    { "+", delegate(double x, double y) { return x + y; } },
    { "-", delegate(double x, double y) { return x - y; } },
    { "*", this.DoMultiplication },
    { "/", this.DoDivision },
    

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

    На помощь приходит C# 3.0 с лямбдами:
    private Dictionary<string, Func<double, double, double>> _operations =
    	new Dictionary<string, Func<double, double, double>>
    	{
    		{ "+", (x, y) => x + y },
    		{ "-", (x, y) => x - y },
    		{ "*", this.DoMultiplication },
    		{ "/", this.DoDivision },
    	};
    

    Во-о-т, уже гораздо лучше — девушки уже строчат комменты!

    Func<double, double, double> эквивалентно delegate double Delegate(double x, double y)
    

    Сигнатура фанка читается как Func<тип первого аргумента, тип второго аргумента, тип результата>. Сам по себе Func это тот же делегат, но с генериками. Помимо удобства записи, Func принимает как лямбды, так и анонимные методы, так и обычные методы и всё под одним обьявлением. Разве это не удивительно удобно?

    Итог
    Чего же мы добились? Ключевой метод PerformOperation сократился до разумной длины.
    public double PerformOperation(string op, double x, double y)
    {
    	if (!_operations.ContainsKey(op))
    		throw new ArgumentException(string.Format("Operation {0} is invalid", op), "op");
    	return _operations[op](x, y);
    }
    


    Словарь operations можно расширять как угодно. Можно при желании создавать его из xml-файла, из фабрики калькуляторов, да хоть из текста введенного пользователем. Лишь бы операции приводились к типу Func<double, double, double>.

    Таким образом в C# можно писать изящные, типизированные конструкции практически без лишней воды.

    Уголок любителя динамических языков
    А вот в JavaScript я всегда мог написать
    var operations = { "+": function(x, y) { return x + y; } };
    

    Нафига спрашивается всякие фанки-шманки?

    Отвечаю: C# это сильно-типизированный язык, который строго следит за тем чтобы типы совпадали и не падали в рантайме. За попытку присвоения неправильных типов, он бьет по рукам при компиляции. Поэтому ему требуется формальное указание о всех фигурирующих типах.

    Апдейт, где я срываю покровы.
    Переписав наш PerformOperation на использование делегатов мы можем расширить функциональность калькулятора. Добавим метод DefineOperation в класс Calculator:
    public void DefineOperation(string op, Func<double, double, double> body)
    {
    	if (_operations.ContainsKey(op))
    		throw new ArgumentException(string.Format("Operation {0} already exists", op), "op");
    	_operations.Add(op, body);
    }
    

    Теперь для наглядности добавим операцию взятия по модулю:
    var calc = new Calculator();
    calc.DefineOperation("mod", (x, y) => x % y);
    var mod = calc.PerformOperation("mod", 3.0, 2.0);
    Assert.AreEqual(1.0, mod);
    

    Сделать такой же трюк невозможно без изменения кода калькулятора если PerformOperation использует switch.
    Поделиться публикацией

    Комментарии 181

      0
      Зачем вызывать методы своего класса через this (this.DoAddition(x, y);)?
        +3
        Потому что если у класса контейнера есть статический метод с таким именем, он может вызваться. А с this понятно что ты вызываешь у текущего объекта.
          +6
          Не может. C# не позволит создать статический и instance-метод с одной сигнатурой.
            0
            эмм?
            using System;
            
            namespace TestCSharp
            {
            	class Container {
            		public static void a(){
            			Console.WriteLine("static");
            		}
            		
            		public class subContainer {
            			public void a()
            			{
            				Console.WriteLine("non static");
            			}
            			
            			public void b(){
            				a();
            			}
            		}
            	}
            	class MainClass
            	{
            		public static void Main (string[] args)
            		{
            			Container.subContainer tmp = new Container.subContainer();
            			tmp.b();
            		}
            	}
            }
            


            До студии не добрался, в mono прекрасно отработало. Если закомментировать a() из subContainer то вызовется статический метод из Container.
              +1
              Эээ, у вас nested-классы, это вообще другой разговор, там с видимостью всегда проблемы. Сделайте a() статическим во вложенном классе — и получите еще более неприятную ситуацию.

              (в частности, именно поэтому nested-классы не входят в набор рекомендованных практик)
                0
                Я описывал именно случай с классом контейнером.
                  +1
                  Вот только в статье его нет, и поэтому оправдать им this никак не выходит.
                    0
                    В том то и дело, что this не надо оправдывать. this работает ожидаемо во всех случаях. А вот отказ от this еще надо оправдать.
                      +1
                      Лень писать :) Ни разу не пригодилось, не было ошибок, связанных со смешения статического и не-статического контекстов. Если можно не писать — почему бы этого не делать. C# и без того не особо компактный язык.
                +1
                Очевидно, в студии отработает так же.

                Но:
                — во-первых, такие вещи надо знать;
                — во-вторых, РеШарпер подскажет, что «Method… hides method from outer class»;
                — и, в третьих, возможно придется посидеть с дебаггером некоторое время, если сталкиваемся с этим впервые.
                  0
                  Именно поэтому проще и надежнее писать this. Никаких негативных последствий this перед вызовом я не вижу.

                  Про «такие вещи надо знать» уже почти год не пишу на шарпе, так что был немного обескуражен, и был уверен, что ничего не поменялось.
                    0
                    Ну, никаких негативных последствий от «не писать this.» тоже нет, если все делать с умом.
                      0
                      Во первых не все зависит от разработчика. Часто приходится поддерживать существующий Code Base. Я сталкивался с кодом выше, и мой код будет работать независимо от того как был написан другой код.
                      Ну и в вторых, если бы всегда и всем можно было писать с умом, то ни IDE ни строго-типизированные языки были бы не нужны.
                        0
                        Очевидно, это дело личных предпочтений. А о вкусах не спорят.
            0
            1. IntelliSense
            2. StyleCop — stylecop.soyuz5.com/SA1101.html
              +1
              Не знаю, как у остальных, но SA1101 — одно из правил, убиваемых (suppress) первым же — за навязчивую избыточность.
              –1
              Просто это стандарт такой в фирме где я работаю. Привился.
                0
                Быть может, StyleCop`ом код чистили? По-умолчанию в правилах прописано использовать «this» для методов и свойств экземпляра.
                  0
                  Быть может, просто StyleCop`ом код чистили? По-умолчанию в его правилах прописано использовать «this» для методов и свойств экземпляра.
                  0
                  Поясните для знающих C++: делегаты в C# это полный аналог std::function? Т. е. можно в одном месте использовать указатели на функции, функторы (оно есть в c#?) и с не очень давних пор лямбда-функции (C++11). Так я понял из статьи.
                    +2
                    В C# на уровне языка нет «указателей на функции» в понимании C++. Есть методы, лямбды и делегаты (они же — функторы).
                      +1
                      Да, делегаты это полный аналог std::function
                      +2
                      А не могли бы вы поточнее указать, где и как именно вы решили озвученные вами ранее проблемы, а именно:
                      — Софт изменчив. Завтра вам понадобится добавить взятие по модулю и тогда придется перекомпилировать класс.
                      — Код в текущем виде не имеет никаких проверок входных данных.
                        0
                        См. пример в апдейте поста. Ну а проверки входных данных я опустил, это же пример всё-таки.
                        +4
                        C# это строго-типизированный язык, который строго следит за тем чтобы типы совпадали и не падали в рантайме. За попытку присвоения неправильных типов, он бьет по рукам при компиляции. Поэтому ему требуется формальное указание о всех фигурирующих типах.

                        Простите, а Вы строгую типизацию со статической здесь не путаете?
                          +5
                          Пишете ключевое слово dynamic и она становится динамической.
                            0
                            Типизация бывает разной: сильной, слабой, статической, динамической, и т.д.. Строгой не бывает — это я в спешке придумал. :)

                            То что я имел ввиду, это в CLR типизация сильная и статическая (с недавних пор можно использовать dynamic и сделать её слабой и динамической).
                              +1
                              Сильная == строгая. Но пары (сильная\слабая) и (динамическая\статическая) ортогональны. Я понял, что Вы имели в виду. Это все моя дурацкая привычка придираться )
                            +20
                            (а) поздравляю вас, вы изобрели паттерн «стратегия» (и это прекрасно видно в вашем «срываю покровы», где становится понятно, что можно было бы сразу передать делегат, а не регистрировать его через DefineOperation)
                            (б) то, что вы использовали для выбора стратегии, называется «табличный метод»
                            (ц) лямбды появились в C# 3
                            (д) C# _не требуется_ «формальное указание о всех фигурирующих типах» — смотрите на ключевое слово var начиная с C# 3.
                            (е) «Помимо удобства записи, Func принимает как лямбды, так и анонимные методы, так и обычные методы и всё под одним обьявлением. Разве это не удивительно удобно?»
                            Удивителен тот факт, что вы не знаете, что это верно для любого определения делегата, Func просто предоставлен нам BCL. «Анонимный метод» — это синтаксический сахар, превращающийся во время компиляции в обычный метод. «Лямбда» — синтаксический сахар, превращающийся во время компиляции в анонимный метод (и дальше в обычный метод) либо в AST (в зависимости от типа выражения).

                            Ну и последнее. Очень круто, конечно, что список операций можно менять как угодно, а в PerformOperation — всего одна строчка, но вот определить поведение этого PerformOperation в один взгляд теперь практически невозможно. А код надо писать для тех, кто его будет читать. Так что прежде чем внедрять у себя такое — четыре раза подумайте, хотите ли вы, чтобы в этом разбирался тот маньяк с бензопилой, который будет читать это следующим (и он знает, где вы живете!).
                              0
                              Хочу отметить, что var, не отменяет четкую типизацию.

                              оператор лишь избавляет вас от избыточности кода. Если не использовать Var, то избыточность очень наглядна в следующем примере:
                              Dictionary<int,string> dic = new Dctionary<int,string>();

                              В случае с методами, переменная var примет тот тип, который возвращает функция.
                              var result = Function.SomeMethod();

                              Очень удобно использовать var с длинными сигнатурами типов и в купе с LINQ.
                                +2
                                Вот этого всегда сторонился.
                                Например:
                                Dictionary<int,string> dic = new Dctionary<int,string>();
                                заменяется на:
                                var dic = new Dctionary<int,string>();
                                т.к. тут понятно, что мы ньюкаем.

                                А вот:
                                var result = Function.SomeMethod();
                                Не понятно. Хорошо компилятору, он умный. А я нет. Не хочу помнить наизусть, что SomMethod возвращает. И когда коллеги повально кругом этот var используют, мне сложнее в коде разобраться.

                                Может я не прав? Хочу узнать аргументацию против. Потому что я очень редко var использую. В случае, когда только действительно точный тип для восприятия не важен — результат линькью выражения, например.
                                  0
                                  Для читаемости кода хорошо помогает правильное именование переменных.
                                  Я раньше тоже сторонился var, но постепенно втянулся и сейчас использую повсеместно.

                                    0
                                    Я тоже не люблю var. Трудно читать код, особенно неизвестный, когда везде стоят var'ы. Чтобы узнать какого типа переменная, приходится наводить мышкой на методы.
                                    +4
                                    По этому поводу все написано у Липперта: blogs.msdn.com/b/ericlippert/archive/2011/04/20/uses-and-misuses-of-implicit-typing.aspx

                                    Если вкратце, точный тип выражения не важен почти никогда, важна его семантика. Представьте себе, что вы смотрите на этот result страницей ниже, уже не видя его объявления. Вот вам важно, объявили его явно, или же неявно? Все одно придется использовать дополнительные средства для его выяснения, а им все равно. Существенно важнее, _что именно_ это за результат (кстати, именно поэтому за именование result, если только это не то, что должна вернуть функция, надо бить по рукам).
                                      +1
                                      Конечно.

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

                                      Или если linq выражение возвращает некую коллекцию, то не важно, какую именно. Главное что коллекция и понятен тип элементов.

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

                                      Еще раз этот пример:
                                      var result = Function.SomeMethod();

                                      Непонятно. Но, конечно, я не придираюсь, это не реальный пример. В реальных будут адекватные названия. Но тогда нужны правила именования. Допустим, у нас стандартный пример: типы Figure, Rectangle, Circle.
                                      И вот мы именуем метод:
                                      var result = Function.GetLastCircle();

                                      тип result семантически нам нужен, потому что он определяет круг операций с ним. GetLast — добавили, допустим, для конкретного метода, чтобы объяснить смысл. А вот слово Circle просто обязано быть в имени, чтобы мы поняли, с чем имеем дело. Это хорошо и понятно. Но это выполняет также замена слова var на тип.

                                      Circle result = Function.GetLastCircle();

                                      Я не считаю такое действие лишним. Потому как внесение такого смысла в название метода является хорошим тоном, но никак не проверяется компилятором. Поэтому «доверяй но проверяй» — возможно программист из каких-то других соображений туда это слово поместил или имел ввиду тип из другого пространства имен. А вот явное указание типа переменной снимает последние сомнения.
                                        +1
                                        «var result = Function.GetLastCircle();
                                        тип result семантически нам нужен, потому что он определяет круг операций с ним.»
                                        Почему переменная называется result? Если она называется result, то это то значение, которое вернет тот метод, в скопе которого мы находимся. Значит, ее тип однозначно определен этим методом.

                                        «Но это выполняет также замена слова var на тип.»
                                        Не выполняет. Потому что стоит строчке с декларацией уйти за экран, как вы перестанете видеть тип переменной (а лично я его перестаю видеть в тот момент, когда он уходит за три-пять строчек от анализируемой).

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

                                        Собственно, поэтому ваш пример все настолько же нереален, как и первоначальный. Давайте возьмем реальный метод из реального кода, где, по-вашему, var мешает.
                                          0
                                          На семантика и правила именования идентификаторов компилятор не обращает внимания. Язык программирования можно разбить на две части — то, что имеет значение для компиляции и не имеет.
                                          Задача программистов, как переводчиков — переводить требования в наиболее подходящий текст на языке программирования. И хорошо и правильно уделять внимание семантике, ставить ее на первое место. Но удерживать смысл только в недоступных компилятору местах — нехорошо. Семантика не только в именах, но и в конструкциях. Паттернах, коде. Старый пример. Хотите сделать обход коллекции, используете foreach, а не for. Это говорит читающему ваш код, что вам не важен порядок элементов в коллекции. Семантика также и в типах. Кстати, когда создаете тип, тоже даете имя — и это семантика. Тип — человек, экземпляр Вася.

                                          var Вася
                                          Человек Вася

                                          Второй пример понятнее и более строгий. И тип здесь — элемент семантики. Ведь могло быть и:
                                          Поросенок Вася.

                                          Конечно, можно использовать правила именование:
                                          var ВасяЧеловек;
                                          Но такой подход дает меньше уверенности. И смысла всю семантику переносить в имена тоже нет.
                                          Когда скрол прокрутится, да, видно не будет, что за тип. Точно также как и с var. Но с var его не видно при любом положении скрола.
                                            +1
                                            «Задача программистов, как переводчиков — переводить требования в наиболее подходящий текст на языке программирования.»
                                            Отнюдь. Задача программистов — реализовать поставленную бизнес-задачу.

                                            «var Вася
                                            Человек Вася

                                            Второй пример понятнее и более строгий.»
                                            Вы на шаг дальше-то пройдите?

                                            var Вася = новый Человек();
                                            Человек Вася = новый Человек();

                                            С избыточностью тоже надо бороться.

                                            А главное — в вашем примере отчетливо понятно, что Вася — это Человек. Мне _не нужна_ эта информация, мне достаточно того, что я работаю с Васей.

                                            Я ведь просил конкретный пример, и не зря просил. Потому что в реальности код обычно выглядит так:

                                            x = ПолучитьПлательщика();
                                            x.ОтправитьПлатеж();

                                            И вот какая вам разница, x — это Человек или Мужчина или ПлатящийЧеловек? Важно написать, что это текущийАктивныйПлательщик.

                                            Скажите честно, вы статью Липперта, на которую я дал ссылку, прочитали? Мы говорим о том, что там уже разжевано.
                                              0
                                              «А главное — в вашем примере отчетливо понятно, что Вася — это Человек»

                                              Для меня не отчетливо. Я же написал ниже: имена дают и свиньям. И собакам. А может быть кот Вася. И это разные типы объектов, не обязательно имеющие общего предка. Конечно, если Вася — тип человек, а человек имеет метод «Платить», то любой его потомок — Мужчина, Женщина, ПлатящийЧеловек — имеют метод «платить».

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

                                              «var Вася = новый Человек();
                                              Человек Вася = новый Человек();»

                                              Я изначально говорил, что в таком примере я не имею ничего против var. Тут явно вызов конструктора. А если ссылку на Васю получают вызовом метода, то в этом случае остается только надеяться, что Вася — правильное и понятное имя, выражающее тип (я уже говорил, могут быть и свиньи), а также имя метода — правильное и понятное имя, подразумевающее точно, что у нас возвращается ровно тот тип, который, как мы подозреваем из имени, есть у Васи.
                                              Две вещи подряд, которые не проверяются компилятором и условны. Произведение вероятностей — вероятность правильного ответа падает. Вероятность бага — растет.
                                                0
                                                Послушайте, я уже больше одного раза у вас спрашивал, зачем вам нужно знать _тип_, если для чтения кода достаточно знать _роль_ объекта, а _тип_ нужен только для работы программы, и это проверяется компилятором?

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

                                                Ну и да, если бы вы сначала прочитали Липперта, у вас бы не было этих вопросов.
                                                  0
                                                  Если тип int (т.е. тип, относящийся к реализации, а не предметной области), то не нужен. Всегда достаточно знать _роль_ объекта. Вопрос только в том, откуда вы это знаете. Кастомный тип — это и есть его роль (т.е. возможная работа над ним).

                                                  Я совершенно согласен, что нужно обращать внимание на семантику, а не реализацию. Но типы могут быть элементами семантики. Если я пишу программу для банка, то Платеж — это элемент предметной области. И тип, следовательно, тоже. И я согласен, что сам тип имеет меньшее значение, чем его конкретный экземпляр (которому дают имя). Поэтому я и против венгерской нотации. Но все же даже если у меня есть конкретный экземпляр Платежа, мне не достаточно знать, что это какая-то var рента. Это уточняет — рента — это конкретный Платеж. Но я не уверен, что это вообще он. А именно тип определяет, что я с этим объектом могу делать.

                                                  Тип — это тоже объект, грубо говоря — экземпляр метакласса. И если это не int или decimal, которые никак не относятся к предметной области, то эта информация является семантической. Это не «реализации». К семантике, как я понимаю, может относиться также и сам код, если его писать правильно (как переводчики, а не реализаторы бизнес-логики. Она не реализуется, а описывается в идеале).

                                                  Как и в естественных языках, для передачи семантики важны не только слова, которым вы дали свои имена, а также и местоимения, запятые и структура предложения.
                                                    0
                                                    «Кастомный тип — это и есть его роль (т.е. возможная работа над ним).»
                                                    Нет. Потому что тип — он в рамках всей системы, а роль — в рамках конкретного контекста.

                                                    Вы Липперта-то прочитайте, там все с примерами разобрано.

                                                    «А именно тип определяет, что я с этим объектом могу делать.»
                                                    И что, если вы напишете var вместо Платеж, то сможете сделать операцию, не принадлежащую платежу?

                                                    «Как и в естественных языках, для передачи семантики важны не только слова, которым вы дали свои имена, а также и местоимения, запятые и структура предложения. „
                                                    Ээээ… у вас тут дикая мешанина, не имеющая отношения к тому, что в реальности происходит в естественном языке. Давайте не будем туда лезть, хорошо?
                                                      0
                                                      «И что, если вы напишете var вместо Платеж, то сможете сделать операцию, не принадлежащую платежу?»

                                                      Не смогу. Я и не спорю — статическая типизация не нарушается. Если я напишу var то я буду плохо понимать, что это. И мне придется больше времени въезжать, какую операцию я могу с именем сделать, а какую нет.

                                                      Хорошо, я почитаю статью, а то нехорошо как-то спорить без этого
                                                        0
                                                        «Если я напишу var то я буду плохо понимать, что это.»
                                                        Значит вы плохо себе представляете, что у вас происходит в этом куске кода.

                                                        «И мне придется больше времени въезжать, какую операцию я могу с именем сделать, а какую нет.»
                                                        Интеллисенс сделан именно для этого. Вы же не помните наизусть все 100500 методов Queryable?
                                                          0
                                                          «Значит вы плохо себе представляете, что у вас происходит в этом куске кода.»

                                                          Конечно. У меня цель — прочитать чужой код. Я его плохо понимаю изначально и хочу понять, что он делает и зачем. О том и речь. Мы спорим о вкусах. По вашему получается, что заменив тип на слово var, код становится читабельнее. Вы думаете, что уменьшив информацию, вы убрали ненужную информацию — шум, которая отвлекает от сути.
                                                          А я считаю, что это в большинстве случаев не шум. Опыт у меня такой. Мне тяжелее читать чужой код, где кругом пользуются var. У нас есть любители. И даже мой код правят, меняя типы на var.

                                                          Цель написания кода — чтобы его читали. Вот, я один из народа, у которого есть личный негатив и трудности со чтением реального кода. Можно, конечно, поспорить, что имена плохие давали, что причина не в var, а в названиях экземпляров. Может быть. Но глаза мои не сломались бы, если бы перед хорошими именами экземляров были прописаны хорошие имена классов.
                                                            0
                                                            «Вы думаете, что уменьшив информацию, вы убрали ненужную информацию — шум, которая отвлекает от сути.»
                                                            Это не (только) я так думаю, это много кто так думает.

                                                            «Но глаза мои не сломались бы, если бы перед хорошими именами экземляров были прописаны хорошие имена классов.»
                                                            А мои — ломаются. И этому есть вполне конкретное объяснение: смешение реализации и намерения.
                                                    0
                                                    >зачем вам нужно знать _тип_, если для чтения кода достаточно знать _роль_ объекта

                                                    Возвращаясь к примеру

                                                    var obj = doSmthUseful(arg1, arg2, arg3);

                                                    Я так понимаю, читая унаследованный код, роль мы должны узнать из имени (в данном случае doSmthUseful)?
                                                    Теперь вопрос: а где гарантия что предыдущие авторы кода были достаточно сознательный для того чтоб придумать адекватное имя методу, которое бы объясняло бизнес-роль объекта obj?
                                                      0
                                                      «Я так понимаю, читая унаследованный код, роль мы должны узнать из имени (в данном случае doSmthUseful)?»
                                                      Нет, роль объекта вы должны узнать из obj.

                                                      «а где гарантия что предыдущие авторы кода были достаточно сознательный для того чтоб придумать адекватное имя методу, которое бы объясняло бизнес-роль объекта obj? „
                                                      Там же, где и гарантия, что предыдущие авторы были достаточно сознательны, чтобы придумать адекватное имя классу.
                                                        0
                                                        Ну тут можно конечно поспорить о том, что классы обычно дизайнят более продвинутые разработчики, чем те, которые их используют. Но всё-равно, лично я не понимаю, зачем скрывать детали. Ведь, как завещал Гвидо ван Россум, explicit is better then implicit.
                                                          0
                                                          «Ну тут можно конечно поспорить о том, что классы обычно дизайнят более продвинутые разработчики, чем те, которые их используют.»
                                                          Так, извините, метод класса — такая же часть его дизайна, как и его имя. Так что гарантия остается там же.

                                                          «Но всё-равно, лично я не понимаю, зачем скрывать детали.»
                                                          Чтобы уменьшить количество информации, которую мы обрабатываем в момент времени. Programming is about managing complexity, как завещал МакКоннел.
                                                            0
                                                            Ок, уменьшили

                                                            catpad.net/michael/apl/

                                                            Понятнее стало?

                                                            Думаю, он имел в виду другое, например, уровни абстракции.
                                                              0
                                                              Что он имел в виду, описано в книге. Пересказывать это достаточно бессмысленно, там все равно написано лучше.

                                                              Кстати, а вы прочитали статью Липперта, упомянутую в этом треде, где, помимо прочего дается ответ на вопрос «зачем скрывать детали»?
                                                                0
                                                                Честно говоря, я впервые по другую сторону баррикады. Обычно я доказываю всем, как важна семантика и смысл, и как не играют роль реализации.

                                                                Но в данном вопросе мы разошлись во мнениях, что является семантикой.
                                                                  0
                                                                  Прочитал. Но не убедился. Инкапсуляция, конечно, хорошо, но использовать var ради идеи инкапсуляции — это, имхо, перебор. Тогда логичнее уж использовать

                                                                  SomeInterface obj = ...

                                                                  Инкапсуляция тоже будет в полной мере соблюдена без ущерба для понимаемости кода.

                                                                  А вообще выглядит как — сделали спорную фичу (ориентированную, имхо, на wow-фактор в первую очередь), а когда народ призадумался, так ли оно надо, и какую проблему это решило — подключили толпу евангелистов чтоб те разъяснили, что, оказывается, мы жить не можем без этой функции. Странно, а как до этого-то жили.

                                                                  (имхо)
                                                                    0
                                                                    "
                                                                    SomeInterface obj =…

                                                                    Инкапсуляция тоже будет в полной мере соблюдена без ущерба для понимаемости кода."
                                                                    Это совсем другое. Это ничем не отличается от указания типа (собственно, интерфейс и есть тип).

                                                                    «сделали спорную фичу (ориентированную, имхо, на wow-фактор в первую очередь)»
                                                                    Эта фича в первую очередь ориентирована на выражения и анонимные классы. Собственно, без нее работать с анонимными классами (в C#) невозможно.

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

                                                                    В данном случае евангелист (точнее, один из авторов языка) объясняет, для чего _кроме_ анонимных типов это надо/полезно применять. И вызвано это объяснение тем, что этот вопрос регулярно задают.
                                                                      0
                                                                      Ну вот логично было бы разрешить var только для случая использования анонимных типов и только для него.
                                                                        0
                                                                        Тогда бы люди спрашивали «почему нельзя было сделать var для всего? Неужели компилятор такой тупой, что не может сам догадаться о типе?»
                                                                          0
                                                                          А что в этом логичного? Type inference прекрасно работает и не только там.

                                                                          (Вы, кстати, не видели type inference в каком-нибудь f#? Там все существенно страшнее выглядит с непривычки.)
                                                                            0
                                                                            То что для анонимных типов это оправдано. А для остальных случаев можно было оставить явный вид, сократив многословность другим способом, например

                                                                            Dictionary<string, List<decimal>> prices = new Dictionary<string, List<decimal>>();
                                                                            ->
                                                                            Dictionary<string, List<decimal>> prices = new Dictionary<>();


                                                                            >Вы, кстати, не видели type inference в каком-нибудь f#

                                                                            Видел в Mercury. Думаю, там нечто похожее =)
                                                                              0
                                                                              Ваша запись ничем не лучше var, если честно. А уж что она будет делать в случае ковариантности, мне и вовсе страшно представить (но там и var нельзя применять, будем честными).

                                                                              Но по большому счету, для меня личнор все аргументы против var закончились в тот момент, когда я мысленно заменил переменную на поле (то есть var smth = smthElse() на _smth = smthElse()), и понял, что в этом случае мы давно и прекрасно обходимся без указания типа в поле зрения.
                                                                          0
                                                                          «SomeInterface obj =

                                                                          Это совсем другое. Это ничем не отличается от указания типа (собственно, интерфейс и есть тип).
                                                                          »

                                                                          Тут и я поспорю. Информационной ценности что там что там. Кстати, с выводами статьи Липпера вполне согласен. Я же сразу писал, что иногда использую var, когда действительно тип не имеет значения. Когда в запросе линькю меня никак не волнует, какой там список — кверибил или просто IEnumerator, потому что дальше я просто перебираю элементы или делаю поиск элемента. Кишки действительно не интересны и не нужны.
                                                                          Но возвращаемое значение метода для меня не очень понятно по типу. Вот здесь тип уместен. Если это кастомный тип и отражает важную часть предметной области. Интелисенс — не выход. Я хочу читать код, а не клацать мышкой, чтобы найти что-то скрытое. Не считаю, что эта инфа лишняя, она элемент семантики. Иногда типы — это части реализации. Особенно, где нет кастомных. В С, например. int, float, double — какое мне дело, сколько он байт выделяет? Тут не надо. Но когда я должен знать, платеж это, или должник, или клиентБанка, то имен экземпляров не достаточно лично для меня.

                                                                          Я интуитивно всегда за то, чтобы язык использовали, как лингвистическую вещь, которая описывает задачу, а не решение задачи. И вот, что получится, например:

                                                                          var yesterdayRent = GetRent(DateTime.Now.AddDays(-1));

                                                                          Рента — это тип Paymant. Откуда я из кода выше это знаю? Если делать перевод на наш язык, звучит примерно так:

                                                                          Некая/нечто рента, взятая за вчера.

                                                                          А так:

                                                                          Payment yesterdayRent = GetRent(DateTime.Now.AddDays(-1));

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

                                                                          В первом случае, да, допустим, я тоже понял слово рента. Но я не знаю, что это точно. Может быть это класс, содержащий распределение цен на жилье/офисы географически. А может быть и по времени средние цены. Я знаю только слово, которое меня приблизило к смыслу, но не дало мне понимания, что за тип и что с ним можно делать.
                                                                            0
                                                                            «var yesterdayRent = GetRent(DateTime.Now.AddDays(-1));

                                                                            Рента — это тип Paymant. Откуда я из кода выше это знаю? Если делать перевод на наш язык, звучит примерно так:

                                                                            Некая/нечто рента, взятая за вчера.»
                                                                            Если вы не знаете, что такое рента, то вы не знаете business domain. Если вы не знаете домен, то вы все равно не разберетесь.

                                                                            «Вчерашняя рента, которая является платежом. (тут меня осеняет, что это платеж, а значит даже не зная этого класса, я догадываюсь, что в нем есть атрибуты — кто платил, когда платил и т.д.).»
                                                                            А зачем вам это знание в дальнейшем коде?

                                                                            Поймите, если речь идет о том, чтобы _читать_ код, то вам нужно увидеть, что кто-то получил ренту за вчера, а потом что-то с ней сделал. И то, что он с ней сделал — явно написано в коде. А что _еще_ с ней можно сделать — совершенно не важно.
                                                                              0
                                                                              «Если вы не знаете, что такое рента, то вы не знаете business domain.»

                                                                              Так вот код и есть та книжка, которую можно читать при некотором подходе к программированию. Языки стремятся в декларативную сторону, отвязаться от реализации. Поэтому код и есть документом, а не реализацией. (то, что вы защищаете). Поэтому я его хочу читать как книгу и даже если у меня есть пробелы в предметной области, хочу по максимуму понимать написанное в коде, а не в дополнительных документациях к коду. Требования к задаче и сама решение по возможности должны быть равны — и то и другое описание.

                                                                              «А зачем вам это знание в дальнейшем коде?»

                                                                              Для понимания. Я никак не могу вам объяснить, что типы — не только бывают с реализацией связаны, но и с описаниями. Мой мозг например, плохо работает, когда я читаю книгу, допустим по математике или физике, а там пишут формулой или термин, еще не встречавшийся и говорят: «а что это, мы узнаем в следующей главе». И оперируют на всю формулой или термином. Мозг отключается и абсолютно отказывается читать дальше.

                                                                              Также и здесь. Я последовательно воспринимаю информацию. Очень плохо воспринимаю просто слово, а потом через куски кода можно понять, что это такое. Описание типа перед именем осознается, как привязка слова к некоторому объекту. Слово не висит в воздухе, ожидая, что я потом его свяжу с объектом.
                                                                                0
                                                                                «Так вот код и есть та книжка, которую можно читать при некотором подходе к программированию. „
                                                                                Чтобы читать книжку, нужно знать язык, на котором она написана. Business dictionary (ubiquitous language) — обязательный пререквизит для чтения кода.

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

                                                                                Что это такое? Рента.
                                                                                Что такое рента? Посмотри в словарь (в конце концов, это ничем не отличается от «что такое платеж» или от класса, названного «рента»).
                                                                                Что с ним происходит? Посмотри в код.

                                                                                Это строго линейно, если что.
                                                                                  0
                                                                                  Не согласен по всем пунктам.

                                                                                  Чем отличается 1) собака от 2) собака. Первая состоит из 6 букв, вторая из четырех лап, хвоста и рычалки. Слово и объект, с которым оно связано — разные по сути вещи. И линейное восприятие информации — это последовательное присвоение имен (терминов) объектам или уже созданным терминам.

                                                                                  var rent = GetRent(...);
                                                                                  Говорит: нечто мы возьмем с помощью метода GetRent и назовем рентой. Здесь rent — это слово (привет кэпу). И здесь тоже:

                                                                                  Payment rent = GetRent(...);
                                                                                  Но Payment — уже не просто слово. Словом оно было здесь:
                                                                                  class Payment
                                                                                  {
                                                                                  }
                                                                                  Что читается, как «пусть Оплатой мы будем считать...». Мы в этом месте определили суть слова Payment. Присвоили имени суть.

                                                                                  В этом же месте впервые появляется новое слово:
                                                                                  Payment rent = GetRent(...);
                                                                                  И читается так:
                                                                                  мы получили оплату методом ВзятьРенту, которую назовем рентой в данном скоупе.

                                                                                  Этот код говорит:
                                                                                  var rent = GetRent(...);
                                                                                  Мы получили нечто методом ВзятьРенту и пока пусть будет нечто, позже станет понятно, какие методы оно пользует. Если непонятно, то посмотри в словарь. Если непонятно дальше, посмотри на интелисенс и перейди, посмотри класс.

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

                                                                                  Я утрирую. Но в общем, язык программирования — это язык описаний. Он иногда неуклюж, но языки движутся в эту сторону. Код — единственный наш друг, который не соврет. Любая документация (ЮМЛ, требования) — являются вторичными и необязательными. Они могут быть и могут дополнять. Но только код является первичным и обязательным. Требования можно писать тестами. Тесты — это утверждения. Чем не язык?

                                                                                  В общем в будущем думаю так и будет. Описали задачу, и она уже работает. Два раза не описывают. А пока, думаю, к этому надо стремиться. Хотя бы с помощью шарпа.
                                                                                    0
                                                                                    «В этом же месте впервые появляется новое слово:
                                                                                    Payment rent = GetRent(...);
                                                                                    И читается так:
                                                                                    мы получили оплату методом ВзятьРенту, которую назовем рентой в данном скоупе. „
                                                                                    Вот только что такое “оплата», вы не знаете, потому что определение класса в другом месте. Поэтому вместо одного термина (рента) вы получили два (рента и оплата), причем из разных доменов. И это, заметим, простой пример, мы еще не брали словари и коллекции.

                                                                                    «Мы получили нечто методом ВзятьРенту и пока пусть будет нечто, позже станет понятно, какие методы оно пользует.»
                                                                                    Я еще раз спрашиваю — зачем нам знать, какие методы оно использует? Нам надо знать, _что_ оно такое. Оно — рента. Рента — это частный случай платежа.

                                                                                    Представьте себе, что в вашем примере написано Rent rent = GetRent() (а Rent — это Rent: Payment). Что вы теперь будете делать?

                                                                                    Весь ваш пример построен на том предположении, что вы знаете, что такое Payment. А откуда вы это знаете?

                                                                                    «Почему словарь (обязательный реквизит для чтения кода) написан в ворде? Что такое ворд? Оно компилится? нет. Оно отражает то, что делает приложение? нет. Чем вообще люди занимались, когда его писали???»
                                                                                    Почитайте Вигерса.

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

                                                                                    BTW, это все можно выразить в аннотациях к коду, но эти аннотации все равно показываются дополнительным инструментом, и никак не зависят от того, что вы напишете, Payment rent или var rent.
                                                                                      0
                                                                                      «Я еще раз спрашиваю — зачем нам знать, какие методы оно использует? Нам надо знать, _что_ оно такое. Оно — рента. Рента — это частный случай платежа.»

                                                                                      Ну да. Нам надо знать _что_оно_такое. Я ж о чем.
                                                                                      var renta = GetRent(...);
                                                                                      никак не говорит _что_оно_такое. Оно говорит: получаем методом ВзятьРенту нечто, чему дадим название «рента». В данном случае — рента — это не понятие пока, это слово. И мало ли, может программист был пьян, когда употребил это слово. Кодом ну никак не определяется. Имя переменной rent как и сама переменная, только здесь впервые появляется. Еще не было «пусть рентой будет Оплата, которая...». Еще этого не было. Все совпадения в фильме с реальными фактами — считать случайными.

                                                                                      «Весь ваш пример построен на том предположении, что вы знаете, что такое Payment»
                                                                                      Payment уже описывалось. Здесь Payment не впервые встречается. Здесь оно уже имеет суть. Я говорю о линейном восприятии. Если оно уже встречалось, значит скорее всего я уже знаю, что это такое. Если не знаю, то перейду на описание этого класса. И это сделать проще. И потом, если снова перейду назад, я не забуду, что рента, это какая-то оплата. Payment в данном случае — это гиперссылка. Понятие уже введено, но если кто не знает, перейти может.

                                                                                      «Если коротко, то потому, что код — это выражение решения задачи, а не терминов, в которых задача выражена.»

                                                                                      Это плохо. Восприятие кода как решения задачи — (имхо) влияет плохо и на процесс кодирования и на архитектуру и всё остальное. Если воспринимать код, как описание задачи — то код будет приближаться к описанию и будет более декларативным. Что и значит — он будет более прямо выражать суть задачи, будет более коротким, более надежным и т.д. и т.п.

                                                                                      «Потому что код не способен выразить «аренда — это процесс использования чужого имущества» и «ИНН — это уникальный номер, присваиваемый налогоплательщику таким-то органом».»

                                                                                      Почему это не способен? Если он не способен выразить мысль, то он не способен и предоставить решение. Разве если есть такие утверждения, то вы вообще не способны закодить такие требования? Очевидно, что способны.
                                                                                      Код условно можно разбивать на две части — декларативную и императивную. Декларативная часть описывает «что», императивная «как». Есть языки, которые только «Что» описывают и это прекрасно.
                                                                                      На вскидку решение задачи двух ваших утверждений. В реляционной БД можно задать таблицы и связи — декларативный способ. Что такое эти связи — это по сути ограничения. Можно взять и создать сто таблиц со ста колонками каждая, тип которых любой (varbinary) и просто пронумеровать, не ставить связи. Очень гибкий вариант, очень большое N-мерное пространство и все точки в нем возможно выразить такими таблицами. Наша задача, удовлетворять требования, т.е. ограничить точки и придумать способ их интерпретации. Способ интерпретации — даем таблицам осмысленные имена, ограничиваем типы колонок нужными типами атрибутов, даем имена. Простраство вариантов сократилось и уже более лучше отвечает задачи. Добавляем связи между сущностями — и еще сократилось. Вот уже довольно неплохо у нас описана предметная область. Имущество, процесс и т.д. (я бд для примера привел, можно и в шарпе такое же сделать). Далее приступаем к недекларативной части и кодируем возможные переходы между состояниями (точками). И если не удалось ограничить типами, проверяем местами на неверные точки и выбрасываем эксепшины.

                                                                                      И вот у нас код выражает требования «аренда — это процесс использования чужого имущества». И т.д. Императивная часть кодирования — похожа на доработку напильником. Иногда из паровоза получаем вертолет.

                                                                                      Такое представление о кодировании, кстати, показывает, что лучше не кодом, а структурами данных выражать предметную область. Т.е. подбирать подходящие типы, которые не позволяют программе быть в запрещенных точках. А также каждая переменная по возможности должна быть независимой от других (ортогональность осей).

                                                                                      Язык программирования — это прежде всего язык. Также, как например, неверно воспринимать математику, как набор формул для решения задач — это в первую очередь язык описания более сложных и точных мыслей. Шарп — это язык более точного описания мыслей, чем естественный язык. И многие говорят, что программы пишут в первую очередь для того, чтобы их читали.

                                                                                      Языки стремятся сделать декларативными. Нас силой заставляют, потому как не захотели мы с рождения на лиспах писать. Декларативные языки не описывают алгоритмы, а описывают задачу «Что делать», а не «как делать». Конечно, это пока красиво в теории, но на практике даже функциональные языки далеки от естественных в простоте описаний.

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

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

                                                                                      Извините за многословность. Но тема такая. Религиозная. Чтобы объяснить идеи, приходится использовать очень далекие аналогии.
                                                                                        0
                                                                                        «var renta = GetRent(...);
                                                                                        никак не говорит _что_оно_такое. Оно говорит: получаем методом ВзятьРенту нечто, чему дадим название «рента». В данном случае — рента — это не понятие пока, это слово.»
                                                                                        В этом ваша ошибка. Здесь рента — это рента, понятие. Названия переменных не случайны.

                                                                                        «И мало ли, может программист был пьян, когда употребил это слово. „
                                                                                        С названиями классов то же самое. Не принимается.

                                                                                        “Payment уже описывалось. „
                                                                                        Где? Кем? Когда? Если это было за пределами скоупа, то я уже забыл, как оно описано (снова привет МакКоннелу и управлению сложностью). Если в скоупе переменной, то это не класс, простите.

                                                                                        “Это плохо. Восприятие кода как решения задачи — (имхо) влияет плохо и на процесс кодирования и на архитектуру и всё остальное. Если воспринимать код, как описание задачи — то код будет приближаться к описанию и будет более декларативным»
                                                                                        Описание задачи не может быть ее решением, вот в чем проблема. Описание задачи — это требования (и требования не могут быть решением). А код — решение задачи, описанной в требованиях.

                                                                                        Требования могут быть описаны (косвенно) в тестах. Но не в коде.

                                                                                        «Почему это не способен?»
                                                                                        Потому что это понятия за пределами домена.

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

                                                                                        «Чтобы объяснить идеи, приходится использовать очень далекие аналогии. „
                                                                                        И это вместо того, чтобы привести один простой пример реального кода, где var мешает.
                                                                                          0
                                                                                          «Описание задачи не может быть ее решением, вот в чем проблема.»

                                                                                          Один из примеров декларативных языков SQL. Вы в коде запроса описываете, что вы хотите. Вы не пишете последовательность шагов и не уговариваете сервер выполнять какие-то последовательности или условия. Поэтому запрос на SQL является описанием задачи и также и ее решением.
                                                                                            0
                                                                                            «Поэтому запрос на SQL является описанием задачи и также и ее решением.»
                                                                                            Он не является описанием задачи в бизнес-смысле — а только об этой задаче есть смысл говорить (привет Липперту снова).

                                                                                            Бизнес-задача: «хочу получить пять самых активных пользователей за сегодня, и для каждого из них — смену позиции по сравнению со вчера». Ее SQL-представление вы можете додумать сами, оно не будет прямым отражением этого текста, и оно будет зависеть от той структуры базы, которую вы выбрали.

                                                                                            Задача — что хочет видеть пользователь.
                                                                                            Ее решение (SQL-код) — источники данных, их связи и преобразования.
                                                                                              0
                                                                                              Если код содержит алгоритм и решает задачу — то он императивный. Если код решает задачу, но в нем не сказано как решать задачу, а только что хотите — он декларативный.
                                                                                              Прямое или непрямое отражение — это субъективные вещи, связанные с культурой. Англичане могут удивляться, что вы прямое слово dog говорите непрямым словом «собака». А когда вы увидете свои требования на китайском, вообще удивитесь.

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

                                                                                              Естественный язык в этом плане далеко не лучше SQL. Если бы у вас осталась только грамматика языка, но язык не имел бы предопределенных понятий, то вам любое ваше требование пришлось бы долго «кодить». Сначала описать элементарные понятия.
                                                                                                0
                                                                                                «Если код содержит алгоритм и решает задачу — то он императивный.»
                                                                                                Ну давайте еще обсудим определение алгоритма.

                                                                                                «Если код решает задачу, но в нем не сказано как решать задачу, а только что хотите — он декларативный. „
                                                                                                Проблема в том, что в моем примере код содержит информацию о том, _как_ решать задачу, и это и есть то, чего я хочу. Так что он, несомненно, декларативный, но при этом он содержит не описание задачи, а ее решение.

                                                                                                “Прямое или непрямое отражение — это субъективные вещи, связанные с культурой. Англичане могут удивляться, что вы прямое слово dog говорите непрямым словом «собака». А когда вы увидете свои требования на китайском, вообще удивитесь.»
                                                                                                Мне казалось, я единожды уже советовал вам не вдаваться в метафоры, связанные с натуральными языками? Простите, но вы написали полную фигню. Англичане не могут и не будут удивляться, узнавая, что в русском собаке соответствует не то же слово, что в английском. Я не удивлюсь, увидя, во что превратились мои требования на китайском. Ну и наконец, «прямое» и «непрямое» — это не субъективные, а вполне объективные вещи, описываемые, в частности, в литературоведении (хотя, конечно, терминология совсем другая). Или вы хотите поговорить со мной о тропах и их разновидностях?

                                                                                                «SQL — это отражение, что хочет пишущий от сервера»
                                                                                                Да. Точно так же как C# — отражение того, что пишущий хочет от CLR. В обоих случаях это не описание задачи, а ее решение.

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

                                                                                                «Сначала описать элементарные понятия. „
                                                                                                Элементарные понятия не описываются, если что. Они элементарные, они просто закрепляются в понятийном аппарате.
                                                                                                  0
                                                                                                  Императивное и декларативное программирование — термины. Не я придумал. И легко погуглить и убедиться, что это такое. Шарп является императивным языком. Условно. Его можно использовать и декларативно. Но он предрасполагает к императивному программированию, потому что сколько не инкапсулируй код в классы — основная работа проходит в методах, где последовательности, циклы и ветвления.

                                                                                                  SQL — декларативный язык. На нем вы пишете «что» вы хотите, а не «как». В этом и мощь SQL. Задачи с помощью него решаются в десятки раз быстрее, ошибок при умении в десятки раз меньше, кода в десятки раз меньше, чем если бы вы писали в шарпе циклами и ветвлениями.

                                                                                                  Я нигде фигню не написал. Аналогии привожу для того, чтобы вы поняли мысль. Не надо аналогии дословно воспринимать и искать в них частные несоответствия. Натуральный язык и язык программирования имеют количественные различия, но не качественные. Так что аналогии уместны. Мне, конечно, тяжело только с помощью натуральных языков объяснить, насколько они разные. Грамматики, логика, похожи. Но они не обязаны быть такими.

                                                                                                  «Что» хочу и «Как» хочу — по смыслу разные вещи. Да, я встречал заказчиков, которые говорили: «хочу, чтобы в таблице была такая колонка, это мое требование». В результате были посланы. Я с такими людьми работать не хочу.

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

                                                                                                  «Элементарные понятия не описываются, если что. Они элементарные, они просто закрепляются в понятийном аппарате. „

                                                                                                  Корабли не плавают, они ходют (аналогия). Закрепление на понятийном уровне не происходит телепатически и невербально. Вот это закрепление и есть описанием.
                                                                                                    0
                                                                                                    «императивное и декларативное программирование — термины. Не я придумал.»
                                                                                                    Вот только вы их употребляете несколько, гм, по-своему. В частности, вы полностью куда-то теряете функциональное программирование.

                                                                                                    «Не надо аналогии дословно воспринимать и искать в них частные несоответствия. „
                                                                                                    Аналогия, построенная с ошибкой в базе — неверная аналогия (и указывает на неверность суждения).

                                                                                                    “Также и «Проблема в том, что в моем примере код содержит информацию о том, _как_ решать задачу, и это и есть то, чего я хочу» — если это так, то это действительно проблема.»
                                                                                                    А как иначе это может быть? Я вам там выше привел требование, реализуйте его на SQL полностью декларативно, таким образом, чтобы решение не содержало ничего избыточного по сравнению с задачей.

                                                                                                    «Закрепление на понятийном уровне не происходит телепатически и невербально.»
                                                                                                    Да ну? Слово «мама» и связанный с ним объект у вас вербально закрепляются? Не смешите меня.
                                                                                                      0
                                                                                                      «В частности, вы полностью куда-то теряете функциональное программирование»

                                                                                                      Где я его потерял? Декларативное программирование — более общее понятие. Обычно включает в себя две парадигмы — функциональное программирование и логическое программирование. Деление достаточно условно. Потому как парадигмы напрямую не относятся к языкам, а только предрасполагают писать в том или другом стиле.
                                                                                                      Функциональное программирование — довольно широкое понятие. В узком смысле — это языки, работающие со списками рекурсивно. Лисп, Хаскель, F# и т.д. В шикором смысле и APL и SQL — функциональные языки. Что не отменяет того, что они же и декларативные языки.

                                                                                                      «Да ну? Слово «мама» и связанный с ним объект у вас вербально закрепляются? Не смешите меня.»

                                                                                                      Да ну? А слово «mother»? У меня и слово «мама» не прошито ни в ДНК, ни в BIOS.

                                                                                                      На счет реализации требований, так просто не стоит тут писать тонны кода. Но все слова в требованиях по сути являются либо существительными (классами), либо глаголами и либо другими частями речи. Естественный язык нечто моделирует. Эти все вещи можно моделировать и языком программирования. Язык программирования не беднее. Логика и способы моделирование с помощью естественного языка вообще-то намного беднее, чем языки математики. Языки программирования — это тоже языки математики. Не знаю, как доказывать эти очевидные вещи.
                                                                                                        0
                                                                                                        «Функциональное программирование — довольно широкое понятие. В узком смысле — это языки, работающие со списками рекурсивно.»
                                                                                                        Понятно. Интересно, откуда вы взяли такое определение.

                                                                                                        «Да ну? А слово «mother»?»
                                                                                                        А слово «mother» не закрепилось, потому что не использовалось в момент закрепления. Это не отменяет того, кто закрепление было невербальным, в этот момент вы еще слов не понимали.

                                                                                                        «Естественный язык нечто моделирует.»
                                                                                                        Нет. Естественный язык что-то описывает.

                                                                                                        «Не знаю, как доказывать эти очевидные вещи.»
                                                                                                        А не надо их доказывать, они не имеют отношения к сути вопроса.

                                                                                                        Если бы требование было своим собственным решением, то весь ваш пафос был бы уместен. Но требование (настоящее, адекватное бизнес-требование) никогда не содержит своего решения. Читайте МакКоннела. Код является выражением (любым способом, декларативным, императивным, функциональным) решения, но не требования.

                                                                                                        И это тоже очевидно, да.
                                                                                                          0
                                                                                                          «Нет. Естественный язык что-то описывает.»

                                                                                                          Описание — частный случай моделирования. Нет?
                                                                                                            0
                                                                                                            Нет.
                                                                                                            0
                                                                                                            «Понятно. Интересно, откуда вы взяли такое определение»

                                                                                                            ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BA%D0%BB%D0%B0%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5

                                                                                                            И не только википедия. Я Макконела читал и вполне уважаю его взгляды. Но Макконала шарпистам надо, по моему мнению, читать критически. Некоторые его советы для нас просто вредны. Защитное программирование, например. Совет хороший для плюсов, для шарпа вреден.
                                                                                                            Кроме Макконела есть и другие книги. Например, Харрисон «Функциональное программирование».
                                                                                                            Потом, личный опыт. Потому как я два года работал профессионально (не просто увлекался), на одном из диалектов APL. Так что имею представления об этих вещах.
                                                                                                            Хотя, конечно, тема обширная, информации много, а времени мало всё изучить.
                                                                                                              0
                                                                                                              «И не только википедия.»
                                                                                                              Вот так у нас и идет вся беседа: на вопрос про определение функционального программирования вы мне даете ссылку про декларативное.

                                                                                                              «Защитное программирование, например. Совет хороший для плюсов, для шарпа вреден.»
                                                                                                              Это почему, интересно?
                                                                                                                0
                                                                                                                А, извините, ошибся в ссылке. Тогда Харрисон. Раньше и в википедии было определение «функциональной парадигмы», где давалось определение основного типа данных — списка, как head::tail.

                                                                                                                Сейчас поправили, дали более общее определение. Я имею такое мнение, потому что знаком с программистами на функциональных языках. И там тоже бывают внутренние холивары, считать ли APL функциональным языком? Поэтому есть два мнения, что такое функциональный язык. Одни люди считают, что есть понятие «чистого функционального языка», которое в современном мире реализовано только на списках и внутри него обязательно лябмда-исчесление. Но при этом, считают, что такой идеал просто не все языки достигают.
                                                                                                                Другие считают, что функциональный язык должен всего лишь выполнять некоторые требования: чистые функции и т.д.

                                                                                                                Понимают функциональное программирование в узком смысле потому, что тема списков хорошо проработана. Даже, если брать Харисона, то большинство глав именно этому посвящено.
                                                                                                                Если же рассматривать APL, то с рекурсиями там не хорошо, а единица данных — н-мерная матрица (массив).

                                                                                                                Вот где-то так.

                                                                                                                «Это почему, интересно? „

                                                                                                                Потому что не связано с задачей. Захламляет код всяческими проверками и выбрасыванием эксепшинов. Дотнет — достаточно умная платформа и он сам бросает в случае чего довольно адекватные эксепшины. Есть сборщик мусора, нет проблем с памятью. В плюсах программа же может не падать, а работать и быть в неопределенном состоянии. Когда обратились к невыделенной памяти, когда работают с указателями напрямую.
                                                                                                                Шарп же упадет в большинстве случаев. Принципы создания надежного ПО как раз в этом заключаются. Когда на шарпе пишут в стиле защитного программирования, то либо стараются сделать наоборот — код с высокой живучестью (очень плохо), либо просто дают другие имена эксепшинам, которые бросил бы код и так. Другие имена — это иногда полезно, но в общем, не думаю, что этим стоит вообще злоупотреблять — код захламляют лишние проверки. Другие имена стоит давать в случае, если это “бизнес-исключение» — т.е. не ошибка кода, а исключение, которое полезно для пользователя (разрыв коннекшина, например).

                                                                                                                Конечно, без проверок и своих эксепшинов не обойтись. Но эту тему я выше обсуждал — лучше создать типы с такими ограничениями, чтобы программа не могла вообще попасть в запрещенные состояния. Тогда проверки типов проведут во время компиляции, а не во время рантайма.
                                                                                                                  0
                                                                                                                  «Раньше и в википедии было определение «функциональной парадигмы», где давалось определение основного типа данных — списка, как head::tail.»
                                                                                                                  Ну понятно. Если бы вы открыли википедию (http://en.wikipedia.org/wiki/Functional_programming), то увидели бы, что определение там есть, и звучит оно следующим образом: «functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data.»

                                                                                                                  И никаких списков.

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

                                                                                                                  «Дотнет — достаточно умная платформа и он сам бросает в случае чего довольно адекватные эксепшины. „
                                                                                                                  Мой опыт показывает обратное: более чем в половине случаев по брошенному исключению нельзя понять причину ошибки.

                                                                                                                  “Когда на шарпе пишут в стиле защитного программирования, то либо стараются сделать наоборот — код с высокой живучестью (очень плохо), „
                                                                                                                  А что плохого-то? Если у вас есть сервис в продуктиве, который должен работать без присмотра месяцами, то он должен обладать высокой живучестью.

                                                                                                                  “Но эту тему я выше обсуждал — лучше создать типы с такими ограничениями, чтобы программа не могла вообще попасть в запрещенные состояния. Тогда проверки типов проведут во время компиляции, а не во время рантайма. „
                                                                                                                  Если коротко, то это невозможно. Попробуйте создать программу, которая не попадает в запрещенное состояние “неверный пароль на соединение».
                                                                                                                    0
                                                                                                                    «То есть входящие аргументы проверять не надо?»

                                                                                                                    Нет. Но это другой холивар. Вызов некоторого метода происходит из другого метода и он находится в какой-то позиции в стеке вызовов. Вот, есть холивар, нужно ли в каждом методе по стеку проверять одни и те же условия, если переменные идут насквозь. Или достаточно один раз? Я думаю, второе. Потом, сам код надо писать так, чтобы он падал, если что. И падал без проверок. Если у вас можно передать параметр в функцию, значение которого запрещено, но код сделан так, что он не упадет и нужно проверить и бросить эксепшин — сигнал, что скорее всего проблемы в архитектуре.

                                                                                                                    «Если коротко, то это невозможно.»

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

                                                                                                                    «А что плохого-то? Если у вас есть сервис в продуктиве, который должен работать без присмотра месяцами, то он должен обладать высокой живучестью.»

                                                                                                                    Чтобы он работал надежно, он должен падать, и привлекать внимание всех, махать всеми флажками и включать сирену. При этом, даже если это непонятное поведение. Код, обладающий высокой живучестью, это код, который подавляет ошибки и работает. Но делает не то, что нужно. Что гораздо опаснее и сулит большие убытки. Багов не так много, и если код создавать, чтобы он падал при первой возможности, а также писать юнит-тесты, то код станет довольно быстро чистым. А код с высокой живучестью может работать годами, приносить убытки, а это могут не замечать. Вот у нас в конторе легаси код такой. Удивительно, как баги работают, созданные в апреле прошлого года.

                                                                                                                    Высокая живучесть полезна только в несущественных вещах — например, гуи, где если лишняя черточка моргнет, никому особо не помещает. Или для взаимодействия с внешними системами — может быть, но под вопросом.
                                                                                                                      0
                                                                                                                      «Вот, есть холивар, нужно ли в каждом методе по стеку проверять одни и те же условия, если переменные идут насквозь. Или достаточно один раз? Я думаю, второе.»
                                                                                                                      Нет никакого холивара, есть известный подход про secure perimeter, и он описан у того же МакКоннела. И это не отменяет того, что во внешнем периметре аргументы надо проверять.

                                                                                                                      «Потом, сам код надо писать так, чтобы он падал, если что. И падал без проверок. Если у вас можно передать параметр в функцию, значение которого запрещено, но код сделан так, что он не упадет и нужно проверить и бросить эксепшин — сигнал, что скорее всего проблемы в архитектуре.»
                                                                                                                      А вы понимаете, что отлавливать ошибку NullReferenceException, кинутую глубоко внутри стека, существенно дороже, чем ArgumentNullException, кинутую на периметре?

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

                                                                                                                      «Чтобы он работал надежно, он должен падать, и привлекать внимание всех, махать всеми флажками и включать сирену.»
                                                                                                                      Если он упал, то он не работает. Требование не выполнено.

                                                                                                                      «Код, обладающий высокой живучестью, это код, который подавляет ошибки и работает.»
                                                                                                                      Не подавляет, а исправляет — делает повторные запросы, использует альтернативные сервера и так далее.

                                                                                                                      «А код с высокой живучестью может работать годами, приносить убытки, а это могут не замечать.»
                                                                                                                      Если этого не замечают, то это никому не важно. Тут все очень просто.

                                                                                                                      Кстати, обратите внимание, как изящно вы вышли из беседы о функциональном программировании.
                                                                                                                        0
                                                                                                                        «А вы понимаете, что отлавливать ошибку NullReferenceException, кинутую глубоко внутри стека, существенно дороже, чем ArgumentNullException, кинутую на периметре?»

                                                                                                                        Нет, не понимаю. У эексепшина есть стектрейс. И найти откуда взялся null, дело минут обычно. А вот строка:

                                                                                                                        if (arg == null)
                                                                                                                        {
                                                                                                                        throw new ArgumentNullException…
                                                                                                                        }

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

                                                                                                                        «Если он упал, то он не работает. Требование не выполнено.»
                                                                                                                        Конечно. А не упал, но работает неправильно — требование тоже не выполнено. Второй случай значительно опаснее, потому что баг продолжает жить и ворует деньги.

                                                                                                                        «Если этого не замечают, то это никому не важно. Тут все очень просто.»
                                                                                                                        )
                                                                                                                        Это не верно. В достаточно сложной системе может быть тяжело видеть утечку денег и неправильную работу.

                                                                                                                        Да, и кстати, это всё у Макконела есть. Не скажу в каких главах, давно читал, но определенно есть.

                                                                                                                        Ошибки надо чинить, а не делать повторные запросы. В редких случаях ошибки — это случайные, не зависящие от самой системы факторы (когда можно сделать дополнительный запрос).

                                                                                                                        «Кстати, обратите внимание, как изящно вы вышли из беседы о функциональном программировании»

                                                                                                                        Тут не вижу смысла продолжать беседу. я не вышел, а в принципе ответил перед этим, что такое общее понимание, и что такое узкое понимание. Вы повторили, что такое общее понимание функциональной парадигмы. Я согласен и всегда был согласен с таким определением. Спорить не о чем.
                                                                                                                          0
                                                                                                                          «Нет, не понимаю. У эексепшина есть стектрейс. И найти откуда взялся null, дело минут обычно. „
                                                                                                                          Вам повезло, если так. В реальности поиски источника данных могут занимать десятки минут под дебаггером, и это вам повезло, если ошибка под дебаггером воспроизводится.

                                                                                                                          “А вот строка:

                                                                                                                          if (arg == null)
                                                                                                                          {
                                                                                                                          throw new ArgumentNullException…
                                                                                                                          }

                                                                                                                          не относится к сути задачи и захламляет код.»
                                                                                                                          Про Code contracts вы не слышали? Равно как и вообще contract-driven development, написание тестов на основании контрактов и так далее?

                                                                                                                          «порождаете неочевидный код, который в потенциале увеличивает количество багов.»
                                                                                                                          Что неочевидного в коде Contract.RequiresNotNull(person)? Какие баги он порождает?

                                                                                                                          «А не упал, но работает неправильно — требование тоже не выполнено. „
                                                                                                                          А с чего вы взяли, что он работает неправильно? Об этом никто не говорил как раз.

                                                                                                                          “Да, и кстати, это всё у Макконела есть. Не скажу в каких главах, давно читал, но определенно есть.»
                                                                                                                          В разделе про defensive programming, который вы считаете ненужным.

                                                                                                                          «Ошибки надо чинить, а не делать повторные запросы.»
                                                                                                                          Это возможно не в каждой системе (особенно в рамках SOA).

                                                                                                                          «Тут не вижу смысла продолжать беседу»
                                                                                                                          Хорошо, а в вопросе «реализуйте конкретную задачу декларативным образом на SQL»?
                                                                                                                            0
                                                                                                                            «Хорошо, а в вопросе «реализуйте конкретную задачу декларативным образом на SQL»»

                                                                                                                            Ваши бизнес-требования? Можем в личной переписке. В форуме писать много кода или схемы, просто показывая, как моделируется предметная область в реляционной БД — думаю лишнее. Реляционная БД — вещь ограниченная, и не всё можно только связями смоделировать. Но очень многое можно. В том числе и процессы и принадлежности сущностей. А вообще, вопрос относился не к SQL и не к базам. На шарпе можно вообще всё моделировать. Базу я привел в пример, потому что она более строгая, что позволяет описать словами сам принцип моделирования. Так проще было отвечать.
                                                                                                                              0
                                                                                                                              «Ваши бизнес-требования?»
                                                                                                                              Уже были: «хочу получить пять самых активных пользователей за сегодня, и для каждого из них — смену позиции по сравнению со вчера».

                                                                                                                              (сразу могу сообщить, что «активность» — это лог операций, производимых пользователем)
                                                                                                                                0
                                                                                                                                Ок. Допустим у нас есть сущность «Активность пользователей», представленная таблицей:
                                                                                                                                create table [dbo].[UserActivity](
                                                                                                                                	[Id] [int] identity(1,1) not null,
                                                                                                                                	[UserId] [int] not null,
                                                                                                                                	[ActivityTime] [datetime] not null,
                                                                                                                                	primary key ([Id])
                                                                                                                                )
                                                                                                                                


                                                                                                                                Вот запрос, решающий безнес-требование:
                                                                                                                                with actWithNums (Position, UserId, ActivityDate)
                                                                                                                                as
                                                                                                                                (
                                                                                                                                	select row_number() over (partition by uac.ActivityDate order by uac.dayActivityCount desc) as Position, uac.UserId, uac.ActivityDate from
                                                                                                                                	(
                                                                                                                                		select uafd.UserId, uafd.ActivityDate, count(*) as dayActivityCount from 
                                                                                                                                		(
                                                                                                                                			select Id, UserId, cast(ActivityTime as date) as ActivityDate from dbo.UserActivity
                                                                                                                                		) uafd
                                                                                                                                		group by uafd.UserId, uafd.ActivityDate
                                                                                                                                	) uac
                                                                                                                                )
                                                                                                                                
                                                                                                                                select todayUsers.UserId, todayUsers.Position as TodayPosition, yesterdayUsers.Position as YesterdayPosition 
                                                                                                                                	from (select * from actWithNums where ActivityDate = cast(getdate() as date)) todayUsers
                                                                                                                                inner join (select * from actWithNums where ActivityDate = cast(dateadd(day, -1, getdate()) as date)) yesterdayUsers on yesterdayUsers.UserId = todayUsers.UserId
                                                                                                                                where todayUsers.Position <= 5
                                                                                                                                


                                                                                                                                Код написан для SQL-SERVER. Не обращайте внимание на звездочки и что я не описываю таблицу пользователей и не джойнюсь к ней. Для примера пойдет.
                                                                                                                                  0
                                                                                                                                  Угу. Вот то, что вы получили — это описание решения. Я легко могу его прочитать: создаем CTE, выбирающий наиболее активных пользователей, затем связываем этот CTE за вчера и за сегодня, и выводим данные.

                                                                                                                                  Но… это уже не описание задачи. В задаче не было ни джойнов, ни CTE, ни группировок — ничего. В задаче был бизнес. Который вы (будем считать для простоты, что правильно) перевели в термины БД и решили там. Точнее говоря, в голове перевели в решение, и это решение изложили терминами БД.

                                                                                                                                  Именно это я и пытался вам показать с самого начала: ваш код на «декларативном» SQL на самом деле оказался описанием решения, а не задачи.

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

                                                                                                                                    И не могу полагать, что он делает СТЕ, причем делает группировку для этого.
                                                                                                                                    То, что вы читаете, как последовательность действий, это ваш способ представления о работе, но никак это не говорит, что SQL сервер в таком порядке выполняет действия.
                                                                                                                                      0
                                                                                                                                      «это не решение, это описание, что я хочу»
                                                                                                                                      Вот только то, что вы хотите, не является явным и прямым отражением требования.

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

                                                                                                                                      Ваше «я хочу» говорит: «я хочу получить данные из таких-то источников, с такой-то обработкой». Это, действительно, декларация, и действительно сервер принимает решения о том, как это сделать, за вас. Но вот бизнес-требование, породившее ваше «я хочу», звучало не так.
                                                                                                                                      0
                                                                                                                                      А попробуйте с конца читать:
                                                                                                                                      «я хочу взять 5 активных пользователей за сегодня и их позиции за вчера, где позициями называем число активностей за день каждого пользователя»

                                                                                                                                      По моему там так написано. А не «создаем СТЕ, выбираем наиболее активных пользователей».
                                                                                                                                        0
                                                                                                                                        Кто мне что-то говорил про линейное чтение? actWithNums это что такое? Какая-то фигня, которая будет определена позже по порядку чтения?

                                                                                                                                        Ну и да, повторюсь, перевод бизнеса в термины SQL — он где-то в вашей голове остался.
                                                                                                                                          0
                                                                                                                                          «actWithNums это что такое? Какая-то фигня, которая будет определена позже по порядку чтения?»

                                                                                                                                          Линейно тоже читается. Читается так:
                                                                                                                                          Пусть actWithNums (возможно не очень адекватное название, но SQL терпит, там большие названия затрудняют чтение) — это представление с колонками Position, UserId, ActivityDate, где Position — это позиция пользователя по количеству активностей за определенную дату ActivityDate. Тогда дай мне пять самых активных пользователей за сегодня, и их позиции за вчера.

                                                                                                                                          Есть разница читать «Просим создать таблицу и наполнить ее группировкой. Потом выбираем из нее с данные и джойним с вчерашними данными». Очевидно, что «создаем» и «это» — разные вещи. А отношение «это» — соответствует работе сервера (мы не знаем последовательность действий, которую он будет выполнять).

                                                                                                                                          На счет линейного восприятия. Аналогия с:
                                                                                                                                          class Paymant
                                                                                                                                          {

                                                                                                                                          Читается — «класс Оплата — это ....». Т.е. здесь вводится новый термин вместе с определением
                                                                                                                                            0
                                                                                                                                            «Пусть actWithNums (возможно не очень адекватное название, но SQL терпит, там большие названия затрудняют чтение) — это представление с колонками Position, UserId, ActivityDate, где Position — это позиция пользователя по количеству активностей за определенную дату ActivityDate.»
                                                                                                                                            Не читается это так. Потому что actWithNums — это представление с двумя вложенными группировками. Понятно, да, что его результирующий смысл именно такой, как нужен по бизнесу, но это _реализация_, а не задача, как она есть. Именно здесь и происходит подмена.

                                                                                                                                            «Читается — «класс Оплата — это ....»»
                                                                                                                                            Как я уже неоднократно говорил, класс обычно определяется не там, где используется, поэтому вся идея линейного чтения идет нафиг.

                                                                                                                                            (а учитывая, что память у меня — как у золотой рыбки, и я определения классов в ней никогда не держу, то и идея с маркерами отправляется туда же)
                                                                                                                                            0
                                                                                                                                            да и потом, не всегда легко читать SQL код. Мы тогда говорили о шарпе. Если шарп позволяет в том случае писать и читать линейно, то зачем эту возможность игнорировать?

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

                                                                                                                                            Функциональные языки следуют парадигме, но не всегда идеальны для чтения. Вот на APL уже кидали ссылку. Но при определенной практике APL будет читаться значительно лучше и быстрее шарпа на одинаковых задачах, в силу того, что код значительно короче.
                                                                                                                                              0
                                                                                                                                              «да и потом, не всегда легко читать SQL код.»
                                                                                                                                              Тогда зачем вы его приводите в качестве примера декларативного языка для описания требований?

                                                                                                                                              «Если шарп позволяет в том случае писать и читать линейно, то зачем эту возможность игнорировать?»
                                                                                                                                              Не позволяет. Что характерно, конкретный пример для C# я тоже просил, и тоже не получил.

                                                                                                                                              «но все же SQL код при определенной практике читается и могу сказать, что читается лучше шарпа в том плане, что выразить на шарпе такой запрос с помощью циклов и ифов (не говорим о функциональном linq), кода понадобится значительно больше, вероятность бага значительно выше и на чтение и понимание всей совокупности кода на шарпе, решающей аналогичную задачу — уйдет больше времени.»
                                                                                                                                              А не надо сравнивать императивное и функциональное (да, именно функциональное, я на любом приличном функциональном языке напишу то же самое, что и на SQL с той же читаемостью) представления. А стоит нам начать писать запрос в C# функционально (linq, кстат, не обязателен), как код станет более читаем, чем в SQL, в силу возможности введения доменных типов и операций.
                                                                                                                                                0
                                                                                                                                                «А стоит нам начать писать запрос в C# функционально (linq, кстат, не обязателен), как код станет более читаем, чем в SQL, в силу возможности введения доменных типов и операций.»

                                                                                                                                                Не станет. Когда я вижу запрос, я вижу весь алгоритм, а не маленький кусочек. Код на шарпе будет значительно длиннее. Следовательно, весь код на шарпе будет тяжелее читаем. Этот можно напрячься и прочитать за пару минут. Без особого опыта за 10. Шарп, со множеством классов и алгоритмов, не будет читаться, уйдет больше времени. Плюс — возможность переполнения стека, вечные циклы — плата за императивное программирования
                                                                                                                                                  0
                                                                                                                                                  «Когда я вижу запрос, я вижу весь алгоритм, а не маленький кусочек. Код на шарпе будет значительно длиннее.»
                                                                                                                                                  Ась?

                                                                                                                                                  GetUserActivity(today)
                                                                                                                                                  .Join(GetUserActivity(today-1)
                                                                                                                                                  , u => u.User.Id
                                                                                                                                                  , u => u.User.Id
                                                                                                                                                  , (cur, prev) => new {User = cur.User, CurrentPosition = cur.Position, PreviousPosition = prev.Position})

                                                                                                                                                  Вот и сравните с вашим кодом на SQL.
                                                                                                                                                    0
                                                                                                                                                    мы по условиям не пользуемся функциональными фишками шарпа ))
                                                                                                                                                      0
                                                                                                                                                      «мы по условиям не пользуемся функциональными фишками шарпа»
                                                                                                                                                      Вы очередной раз невнимательны.

                                                                                                                                                      Цитирую: «А стоит нам начать писать запрос в C# ФУНКЦИОНАЛЬНО (linq, кстат, не обязателен), как код станет более читаем, чем в SQL, в силу возможности введения доменных типов и операций.» Вы отвечали именно на этот пункт.

                                                                                                                                                      «потом, код не аналогичен. Если бы были в активностях в таблице в БД подготовленные позиции, то и делать нечего было бы. Но в БД это плохо.»
                                                                                                                                                      Код как раз аналогичен, потому что GetUserActivity — это аналог вашей CTE, только доменный. «Очевидно, что» где-то внутри себя он делает подготовку позиций, только мы это спрятали (получив, тем самым, уменьшение локальной сложности).
                                                                                                                                                        0
                                                                                                                                                        «Вы очередной раз невнимательны.»

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

                                                                                                                                                        «Код как раз аналогичен, потому что GetUserActivity — это аналог вашей CTE, только доменный. «Очевидно, что» где-то внутри себя он делает подготовку позиций, только мы это спрятали (получив, тем самым, уменьшение локальной сложности). „

                                                                                                                                                        Код будет аналогичен, если и тело того метода будет включено. Это раз. Во вторых прятание кода не упрощает понимание кода. Оно только откладывает разбирательство во всей картине. Т.е. то, что я и говорил: в шарпе код может быть локально проще, но не во всей совокупности. Вы создаете новое имя и за ним прячете код. Это никак не упрощает. Энтропия растет.
                                                                                                                                                          0
                                                                                                                                                          «Не будем придираться.»
                                                                                                                                                          Будем, потому что иначе разговор постоянно напоминает беседу глухого со слепым.

                                                                                                                                                          «Код будет аналогичен, если и тело того метода будет включено.»
                                                                                                                                                          Ну, вы же понимаете, что тело метода — две группировки, и они тоже не менее читаемы, чем SQL? Привести конкретный код?

                                                                                                                                                          «Во вторых прятание кода не упрощает понимание кода.»
                                                                                                                                                          Ууу. Нет, здесь я с вами спорить не буду, читайте МакКоннела, у него это явно описано неоднократно. И все аргументы у него тоже приведены.

                                                                                                                                                          «в шарпе код может быть локально проще, но не во всей совокупности.»
                                                                                                                                                          Вообще-то в совокупности код будет не сложнее SQL. Вам правда пример нужен, вы не можете себе сам представить анонимный делегат?

                                                                                                                                                          «Энтропия растет. „
                                                                                                                                                          Извините, ваше утверждение голословно.
                                                                                                                                                            0
                                                                                                                                                            «Ну, вы же понимаете, что тело метода — две группировки, и они тоже не менее читаемы, чем SQL? Привести конкретный код?»

                                                                                                                                                            Конечно. Если уже сравнивать объем кода и читаемость, то надо предоставлять весь код. Иначе я ведь в SQL тоже мог написать табличну функцию GetUserActivity. И код был бы тривиальный без ее тела.

                                                                                                                                                            Да, я и не сомневаюсь, что очень много кода будет в шарпе, если использовать линкью или методы из него с делегатами. Это функциональный подход. Если использовать F#, так кода еще меньше будет, а читаемость выше — скобочки лишние пропадут.

                                                                                                                                                            «Ууу. Нет, здесь я с вами спорить не буду, читайте МакКоннела, у него это явно описано неоднократно. И все аргументы у него тоже приведены.»

                                                                                                                                                            Скажу еще раз, читал. И что? Его мнение выше законов теории информации? Поясняю. Любое имя — это новая сущность. В смысле бритвы Оккама. Это «плюс» новая информация. Не «минус». Создание новых имен — это всегда выбор из двух зол. Каждое новое имя само по себе увеличивает сложность кода (есть такая мера — цикломатическая сложность". У майкрософт войдет ваш лишний метод). Объем информации в любом случае растет. Что же тогда улучшается? Может (но не факт!!) улучшиться восприятие отдельного куска кода. Потом, нужно смотреть спрятанный код. Чтобы уменьшить необходимость «подглядываний» в спрятанный код, хорошо придумывать имена, связанные с реальным миром, о которых у нас уже есть представление. Но само по себе придумывание имени волшебным образом не съедает информацию. Просто информация выходит за пределы кода (образно). Можно еще писать интерфейсы и описывать словами, что каждый метод делает. Что тоже — выходит информация за пределы кода.

                                                                                                                                                            Так что вот. Имена нужны, чтобы сузить внимание на небольшом числе объектов. Но другие объекты все таки не исчезли. И создание новых имен само по себе зло. Чтобы было меньше необходимости создавать имена, создают более мощные языки. Кстати, и лямбды поэтому и создали, а также Func<>. Чтобы не создавать новое имя — делегат. Чтобы можно было логику описать меньшим объемом текста.

                                                                                                                                                            А есть такие хитрые языки, которые даже лямбды обошли. Там не нужно не только имя методу, но даже и имена входным параметрам. Что делает язык значительно мощнее. Это APL и еще некоторые.
                                                                                                                                                              0
                                                                                                                                                              «Конечно. Если уже сравнивать объем кода и читаемость, то надо предоставлять весь код. Иначе я ведь в SQL тоже мог написать табличну функцию GetUserActivity. И код был бы тривиальный без ее тела.»
                                                                                                                                                              Ну так что ж не написали?

                                                                                                                                                              Вот вам полный код:
                                                                                                                                                              var getUserActivity = (date) => UserActivity
                                                                                                                                                              .Where(u => u.Time.GetDate == date)
                                                                                                                                                              .GroupBy(u => u.User, (u,d) => new {User = u, Activity = d.Count()})
                                                                                                                                                              .OrderByDescending(u => u.Activity)
                                                                                                                                                              .Select((u, Position) => new {u.User, Position});

                                                                                                                                                              getUserActivity(today).Take(5)
                                                                                                                                                              .Join(getUserActivity(today-1)
                                                                                                                                                              , u => u.User.Id
                                                                                                                                                              , u => u.User.Id
                                                                                                                                                              , (cur, prev) => new {User = cur.User, CurrentPosition = cur.Position, PreviousPosition = prev.Position})

                                                                                                                                                              И все.

                                                                                                                                                              «Его мнение выше законов теории информации? „
                                                                                                                                                              Его мнение основано на практике и исследованиях. Люди, к сожалению, не так напрямую подчиняются теории информации, как вам бы того хотелось.

                                                                                                                                                              “Каждое новое имя само по себе увеличивает сложность кода (есть такая мера — цикломатическая сложность»"
                                                                                                                                                              Что, опять? Цикломатическая сложность — «счётное число линейно независимых маршрутов через программный код». При отсутствии точек принятия решений (а метод не является точкой принятия решения) цикломатическая сложность не возрастает.

                                                                                                                                                              Открываем статью (http://blogs.msdn.com/b/zainnab/archive/2011/05/17/code-metrics-cyclomatic-complexity.aspx), читаем, видим, что добавление в код вызова функции циломатическую сложность не увеличивает.

                                                                                                                                                              «Потом, нужно смотреть спрятанный код. „
                                                                                                                                                              Не нужно.

                                                                                                                                                              “Просто информация выходит за пределы кода (образно). „
                                                                                                                                                              Именно в этом и задача — уменьшить количество одновременно воспринимаемой и обрабатываемой человеком информации, потому что возможности мозга ограничены.

                                                                                                                                                              Блин, я, если честно, устал вам пересказывать МакКоннела (а перед этим — Липперта).

                                                                                                                                                              “А есть такие хитрые языки, которые даже лямбды обошли. Там не нужно не только имя методу, но даже и имена входным параметрам. Что делает язык значительно мощнее. Это APL и еще некоторые. „
                                                                                                                                                              Я уже понял, что APL — король всех языков. Но “более мощный язык», в котором не надо давать имена входным параметрам — это круто, да. Верх читаемости.

                                                                                                                                                              Вообще, мне нравится. С одной стороны, новые имена — зло. Но с другой стороны, именно вы требуете создания «новых имен» (указания конкретного класса вместо var), и увеличения тем самым количества информации.

                                                                                                                                                              Очень логично, да.
                                                                                                                                                                0
                                                                                                                                                                «Цикломатическая сложность — «счётное число линейно независимых маршрутов через программный код»»

                                                                                                                                                                Наверное. Но во всех этих объяснениях строится граф только учитывая структуру одного метода. Т.е. в простом случае исследуется структура кода (ифы, свитчи, циклы).
                                                                                                                                                                Элементарная проверка в студии показывает, что при вынесении чего-то в метод — цикломатическая сложность растет.

                                                                                                                                                                «Я уже понял, что APL — король всех языков. Но “более мощный язык», в котором не надо давать имена входным параметрам — это круто, да. Верх читаемости.»

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

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

                                                                                                                                                                Есть еще языки, которые не работают с именами параметров. Например, самый известный — FP.

                                                                                                                                                                «Но с другой стороны, именно вы требуете создания «новых имен» (указания конкретного класса вместо var), и увеличения тем самым количества информации.

                                                                                                                                                                Очень логично, да. „

                                                                                                                                                                Конечно, логично. Когда ставится имя вместо слова var, то оно не создается. Мы указываем уже созданное имя. Вот после var — создается имя — имя экземпляра.
                                                                                                                                                                  0
                                                                                                                                                                  «Не отрицайте очевидное — анонимные методы и лямбды в шарпе именно тем и хороши, что позволяют не вводить ненужные типы (имена), а на ходу сооружать что-то без имени и передавать куда-то.»
                                                                                                                                                                  Это хорошо ровно до тех пор, пока это что-то используется однократно. В нашем случае это неверно. В случае параметров — тоже.

                                                                                                                                                                  «Читаемость скорее — это скорость усвоения информации. Если код очень емкий, то даже медленное чтение символов может дать значительный прирост производительности. „
                                                                                                                                                                  … а так же увеличить вероятность некорректного прочтения. Ну и да, тут простая формула: общая скорость очевидно равна емкости одного символа на скорость чтения символа.

                                                                                                                                                                  “Вы, не зная языка, читать не сможете.»
                                                                                                                                                                  Я ждал этого аргумента.

                                                                                                                                                                  «Те же, кто пользуются апл-подобными языками, читают и работают более производительно»
                                                                                                                                                                  А можно ссылку на реальные исследования? Применительно к реальным бизнес-задачам широкого спектра применимости?

                                                                                                                                                                  «Когда ставится имя вместо слова var, то оно не создается.»
                                                                                                                                                                  Оно просто занимает слот в памяти, да. Количество слотов ограниченно.
                                                                                                                                                                    0
                                                                                                                                                                    «А можно ссылку на реальные исследования?»

                                                                                                                                                                    нет. Личный опыт и слухи.

                                                                                                                                                                    «Применительно к реальным бизнес-задачам широкого спектра применимости?»

                                                                                                                                                                    Тоже не знаю, где такую статистику взять. Эти языки очень хорошо применяются для математических задач, финансовых расчетов, сложных алгоритмических задач — обработки большого объема данных. Людей, пишущих на этих языках немного, не слышал, чтобы их нанимали на другие задачи, вроде ГУИ и бизнес-приложение.
                                                                                                                                                                    Но это языки общего применения. Писать можно что угодно. Вот только более сильные стороны проявляются при работе с большими объемами данных.
                                                                                                                                                                      0
                                                                                                                                                                      «нет. Личный опыт и слухи.»
                                                                                                                                                                      В устах человека, который на APL пишет, это звучит предвзято.

                                                                                                                                                                      «Эти языки очень хорошо применяются для математических задач, финансовых расчетов, сложных алгоритмических задач — обработки большого объема данных. [...] Вот только более сильные стороны проявляются при работе с большими объемами данных. „
                                                                                                                                                                      Т.е., в конкретной задаче, под которую они оптимизированы.

                                                                                                                                                                      ЧТД.
                                                                                                                                                                        0
                                                                                                                                                                        «В устах человека, который на APL пишет, это звучит предвзято.»

                                                                                                                                                                        Да. И что он более быстрый в разработке, слышал от других, пишущих на АПЛ (я не на АПЛ работал, а на одном из его диалектов). Потом, просто есть личный опыт, потому что я работал и с другими языками. Сейчас шарп и sql.
                                                                                                                                                                        Тут или проверить можно или на слово верить. Где статистику взять, не знаю. И проводили ли исследования вообще, в виду, не очень большой распространенности этих языков, тоже вопрос.

                                                                                                                                                                        «Т.е., в конкретной задаче, под которую они оптимизированы.»

                                                                                                                                                                        Они не заточены под задачи. Там принцип работы другой. Само мышление меняется при работе с ними. Главный принцип работы с ним, что нет никаких привычных методов, а есть лишь то, что мы называем операциями в выражениях. Унарными или бинарными. И единица данных — N-мерная матрица. Поэтому и операции все — преобразования матриц. Вы не пишете циклы, а только превращаете одни матрицы в другие.

                                                                                                                                                                        Конечно, это хорошо само по себе для сложных алгоритмов и обработки больших объемов. Написать же обработки событий и воркфлоу бизнес-приложения — тоже наверное не сложно. Но скучно.
                                                                                                                                                                          0
                                                                                                                                                                          «Да. И что он более быстрый в разработке, слышал от других, пишущих на АПЛ (я не на АПЛ работал, а на одном из его диалектов).»
                                                                                                                                                                          Вот когда это скажет человек, который на APL не пишет, и скажет это на основании сравнения team velocity, тогда и будем верить.

                                                                                                                                                                          «Тут или проверить можно или на слово верить»
                                                                                                                                                                          Я в таких вопросах на слово не верю.

                                                                                                                                                                          «Они не заточены под задачи.»
                                                                                                                                                                          Так не бывает.

                                                                                                                                                                          «Главный принцип работы с ним, что нет никаких привычных методов, а есть лишь то, что мы называем операциями в выражениях. Унарными или бинарными. И единица данных — N-мерная матрица. Поэтому и операции все — преобразования матриц. Вы не пишете циклы, а только превращаете одни матрицы в другие.»
                                                                                                                                                                          Обычный (ну ладно, не очень обычный, но все равно) функциональный язык. Со своей нотацией.

                                                                                                                                                                          Ну и что?

                                                                                                                                                                          В рамках своей конкретной задачи он, может быть, и читаем. Но в рамках произвольных задач общего профиля — нет. Иначе бы не изобретали DSL.
                                                                                                                                                                            0
                                                                                                                                                                            Не спорю, что он для чего-то удобнее, для чего-то нет. Даже сказал, для чего он удобнее.

                                                                                                                                                                            И DSL, конечно, очень полезны. Кстати на нем и какое-нибудь DSL вполне можно писать.

                                                                                                                                                                            Я только о том говорю, что это язык общего применения. Также как и шарп. Он не заточен под выполнение чего-то. Сама нотация предрасполагает его использовать для математических задач и для обработки больших массивов.
                                                                                                                                                                              0
                                                                                                                                                                              «Сама нотация предрасполагает его использовать для математических задач и для обработки больших массивов. „
                                                                                                                                                                              Это значит, что и его “высокая читаемость» высока только в этой области.

                                                                                                                                                                              Дальше возвращаемся к вопросу про именованные параметры и так далее.
                                                                                                                                                                                0
                                                                                                                                                                                «Это значит, что и его “высокая читаемость» высока только в этой области»

                                                                                                                                                                                Почему же? Это если сравнивать язык сам с собой. Возможно, на нем элегантно описывать математические задачи. Но хуже описывать другие. Но это не значит, что на других языках эти задачи лучше описываются.
                                                                                                                                                                                  0
                                                                                                                                                                                  «Но это не значит, что на других языках эти задачи лучше описываются. „
                                                                                                                                                                                  Не “значит», но и не доказывает обратного.

                                                                                                                                                                                  Какие-то вещи лучше представляются в функциональной парадигме, какие-то — в императивной; какие-то в виде операций с множествами, какие-то — в виде сообщений.

                                                                                                                                                                                  Для каждой задачи есть свое удобное представление.
                                                                                                                                                                                    0
                                                                                                                                                                                    согласен.

                                                                                                                                                                                    Если что, то на том диалекте, что я работал, есть и ООП (классы, экземпляры) и возможность писать в императивном стиле.

                                                                                                                                                                                    Хотя, предрасполагает писать всё таки в родном стиле, потому как сильные стороны там проявляются
                                                                                                                                                      0
                                                                                                                                                      потом, код не аналогичен. Если бы были в активностях в таблице в БД подготовленные позиции, то и делать нечего было бы. Но в БД это плохо.

                                                                                                                                                        0
                                                                                                                                                        Так что решили?
                                                                                                                                                          0
                                                                                                                                                          ?

                                                                                                                                                          Код не аналогичен. Потом, я указывал, что SQL короче и яснее по сравнению с императивным кодом шарпа. Если пользоваться линкью (или лямдами, понятно, хотя я это не сказал), то код будет более менее одинаковый для восприятия и по объему.

                                                                                                                                                          Плюс бывают и более выразительные языки.

                                                                                                                                                          Решать тут нечего.
                                                                0
                                                                «Скажите честно, вы статью Липперта, на которую я дал ссылку, прочитали? Мы говорим о том, что там уже разжевано.»

                                                                Честно, пока нет. На работе развлекаюсь :)
                                                                Спасибо за статью, обязательно почитаю.
                                                        0
                                                        Плюсую, слава богу, в яве не додумались так сделать.
                                                          0
                                                          я в таких случаях навожу мышку на var и студия подсвечивает, что там за тип. Как альтернативные IDE я не знаю.
                                                        –1
                                                        Я не хочу вступать в холивары, поэтому просто прокомментирую.
                                                        а) Я специально не упоминал про паттерны. Я всего лишь хотел продемонстрировать, почему иногда выгодно передавать исполняемый код как данные.
                                                        б) См. выше.
                                                        в) Спасибо что обратили внимание, сейчас исправлю.
                                                        г) var в шарпе не является аналогом var например, в джаваскрипте. Вы не можете написать var foo;
                                                        д) Почему вы решили, что я этого не знаю? Только потому что у статьи несколько легкомысленный тон, не значит, что я не разбираюсь в сути. Я всего лишь рассмотрел разницу между формами записи делегатов, лямбд и анонимным методов.

                                                        А про маньяка. Определить поведение PerformOperation состоящий из одной строки гораздо проще чем если бы он состоял из нескольких сотен строк. Всё важное в его сигнатуре. И если ко мне прибежит маньяк с бензопилой, я ему популярно разьясню разницу. :)
                                                          0
                                                          «var в шарпе не является аналогом var например, в джаваскрипте. Вы не можете написать var foo;»
                                                          Тем не менее, формальное указание больше не требуется — смотри многократно обсуждаемый пример с методами (и вообще любыми выражениями).

                                                          «Почему вы решили, что я этого не знаю?»
                                                          Потому что из вашего текста можно сделать вывод, что это — уникальное свойство Func. А это не так.

                                                          «Определить поведение PerformOperation состоящий из одной строки гораздо проще чем если бы он состоял из нескольких сотен строк. Всё важное в его сигнатуре.»
                                                          Угу. И это определение — «делает какую-то операцию, определенную где-то еще». Это, в общем-то, и бесит людей с бензопилами.
                                                        0
                                                        Что-то мне подсказывает, что за продемонстрированную гибкость очень дорого заплачено производительностью…
                                                          0
                                                          … и читабельностью. Это стандартный компромис, который нужно решать для каждого конкретного случая. Иногда гибкость существенно важнее.
                                                            0
                                                            Вполне нормально с читабельностью. На счет «поведение этого PerformOperation в один взгляд», то возможно, где-то сложнее. Но шарп, не тот язык, где код и данные — это одно и то же. И слышал где-то рекомендацию, что в императивных языках желательное, где это возможно, заменять структуры кода на структуры данных. Т.е. switch на Dictionary в данном случае. Чтобы не очень разносить заполнение словаря и метод, можно объявлять словарь и заполнять прямо в том же методе, где предполагался switch. При случае будет просто его вынести потом в поле экземпляра. Или еще обобщить — в статическое поле. Или еще в статическое свойство, которое кэширует изменения. И далее вообще сделать расширяющееся поведение в рантайме. Что нельзя было бы сделать с помощью switch

                                                            Это вопрос баланса, как где код расположить, чтобы видно было и понятно. Иногда switch и if предпочтительнее виртуальных методом. Эту же задачу можно решить «стратегией» на виртуальных методах. Суть та же, но кода будет больше и читаемость, думаю, хуже.
                                                              0
                                                              «И слышал где-то рекомендацию, что в императивных языках желательное, где это возможно, заменять структуры кода на структуры данных.»
                                                              Это называется «табличный метод». Описано у МакКоннела в Code Complete. Вместе с пояснениями, где это надо применять, а где — нет. И поверьте, это совсем не «где возможно».

                                                              «И далее вообще сделать расширяющееся поведение в рантайме.»
                                                              А оно нужно кому-то?

                                                              Вот от этого вопроса и надо плясать.
                                                                0
                                                                Табличный метод — это только частный случай. Можно полиморфизм реализовывать виртуальными методами, а можно таблицами делегатов, что, например, внутри С++ и делается.

                                                                А по возможности реализовывать структурами данных, а не структурами кода — это общая рекомендация. Она не всегда уместна. Мы спорим о крайностях. Если нужен свич из 3-5 веток и больше не предвидится, то возможно это не нужно. А если и нужно будет, то можно будет переделать.
                                                                Но при равных условиях читаемости, вариант с данными предпочтительнее.

                                                                «А оно нужно кому-то?

                                                                Вот от этого вопроса и надо плясать. „

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

                                                                И с нечитаемостью не всё очевидно. Заменять структуры кода на структуры данных и код представлять как данные — это мощное средство.
                                                                  0
                                                                  «А по возможности реализовывать структурами данных, а не структурами кода — это общая рекомендация.»
                                                                  С моей точки зрения, это не «общая», а _частная_ рекомендация, применяемая в ограниченном числе случаев.

                                                                  «Но при равных условиях читаемости, вариант с данными предпочтительнее.»
                                                                  Аргументируйте.

                                                                  (хотя равной читаемости не будет никогда, поскольку использование данных вместо кода — это one more level of indirection)

                                                                  «Заменять структуры кода на структуры данных и код представлять как данные — это мощное средство.»
                                                                  Угу. Работающее в конкретных случаях, которых, по моему опыту, меньшинство.
                                                                    0
                                                                    В функциональных языках часто нет разницы между данными и кодом. Можно сказать, что данных нет — всё функции. Вполне общий подход.
                                                                    Шарп не является функциональным языком. Но можно к этому подходить так: языки не связаны с парадигмами, они лишь в некоторой степени предопределяют стиль программирования на них.

                                                                    Шарп вполне разрешает писать в функциональном стиле. Даже если бы в нем не было лямд и анонимных методов, он это бы позволял. Вопрос только в удобстве и читаемости кода.
                                                                    Семантически, если представить опять этот пример и представить С++, как модель внутренней работы виртуальных методов, то стратегия и Dictionary — будет одно и то же. Вы можете написать класс Operation, унаследовать его нескольким классам и в каждом будет по переопределению метода операции. Внутри при компиляции получится такая же таблица с указателями на функции. Делаете стратегию — и вот у вас то же самое.
                                                                    Казалось бы, зачем изобретать велосипед. Но создавая и заполняя Dictionary вы пишете меньше кода и не создаете не очень нужные почти без поведения классы. Второе — такой подход позволяет расширять в рантайме набор действий, не теряя всех прелестей типизации. Третье. Полиморфизм, построенный на передаче делегатов — мощнее ООП средств. Потому что он позволяет сделать всё, что может ООП (пример стратегии), так и много больше.

                                                                    Такая интуитивная аналогия. Тяжело обосновать, но всё же. С помощью объектов вы можете моделировать некоторые вещи. А с помощью делегатов вы можете создавать язык. Т.е. с помощью ООП вы скрываете поведение за именами (инкапсулируете), а с помощью делегатов вы создаете такие ограничения и язык, с помощью которых вы можете делать потом любое поведение, не выходя за рамки. С помощью ООП (на виртуальных методах), по сути, вы кодите более одноразовый код.
                                                                    Ну и свитч и структуры кода — еще более одноразовый код, чем ООП.

                                                                    И повторюсь, я не защищаю крайность, я согласен с Вами, что свитч иногда значительно лучше, потому как он будет проще читаться и соответствовать текущим требованиям. А угадывать и делать заранее гибкий код — неблагодарное дело.

                                                                    «С моей точки зрения, это не «общая», а _частная_ рекомендация, применяемая в ограниченном числе случаев.»
                                                                    Извините, не могу сейчас нарыть ссылку. Кто-то из великих рекомендовал. Причем рекомендации были для языка С. Что сути не меняет.

                                                                    «Угу. Работающее в конкретных случаях, которых, по моему опыту, меньшинство. „
                                                                    Это опять вопрос крайностей. Мощное средство потому и мощное, что с помощью его можно решать как простые задачи, так и сложные. А вот немощное средство сложные задачи не решает. Вопрос только в языке программирования, к чему он предрасполагает и какой код будет более запутанный и сложный для восприятия, а какой — прямое выражение мысли.
                                                                      0
                                                                      «В функциональных языках часто нет разницы между данными и кодом.»
                                                                      Это, простите, почему?
                                                                      Если честно, я перестал понимать, что вы считаете данными, а что — кодом.

                                                                      «Такая интуитивная аналогия. „
                                                                      Ничего интуитивного я в ней не вижу, более того, я ее не понял особо, уж простите.

                                                                      “С помощью ООП (на виртуальных методах), по сути, вы кодите более одноразовый код.
                                                                      Ну и свитч и структуры кода — еще более одноразовый код, чем ООП.»
                                                                      Это прекрасно. Чем более код одноразов, тем более он точен и специфичен. Предварительное обобщение — страшное зло.

                                                                      «Извините, не могу сейчас нарыть ссылку. Кто-то из великих рекомендовал. Причем рекомендации были для языка С. Что сути не меняет.»
                                                                      Ну вот у меня под рукой МакКоннел, в котором явно написано, что привлекательность табличных методов (а это, как мы уже выяснили, частный случай) зависит от сложности кода.

                                                                      «Мощное средство потому и мощное, что с помощью его можно решать как простые задачи, так и сложные. А вот немощное средство сложные задачи не решает.»
                                                                      Ээээ… То есть императивная структура c# для вас недостаточно мощное средство, и есть задачи, которые она не решает, а «замена структур кода на структуры данных» — решает?

                                                                      Знаете, я уже достаточно во всем этом запутался, чтобы попросить конкретный пример.
                                                                        0
                                                                        «у вот у меня под рукой МакКоннел, в котором явно написано, что привлекательность табличных методов (а это, как мы уже выяснили, частный случай) зависит от сложности кода.»
                                                                        Читал я МакКонела. Согласен с ним в данном случае. От языка это зависит. И от сложности кода и задачи. Так не будем и спорить, представляя себе крайние противоположные случаи.

                                                                        «Если честно, я перестал понимать, что вы считаете данными, а что — кодом.»
                                                                        Могу только интуитивные аналогии приводить. Разница между кодом и данными условна.
                                                                        Вспоминаем паскаль. Там есть отличие между процедурами и функциями. Процедуры что-то делают, но значение не возвращают, функции что-то делают и возвращают. С, С++, шарп — проводят обобщение, добавляя тип void. Функциональный подход проводит еще большее обобщение — всё функции, данных нет. Т.е. функция — это значение, которое зависит от входных параметров. Константа — это функция, которая не всегда возвращает одно и то же. Любая программа — это функция — преобразование входных параметров в выходные. Поэтому нет данных. Или можно сказать, что функции — это данные. Список интов — это список из функций, например. Такой подход мощнее.

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

                                                                        И шарп весело шагает в сторону функционального подхода. С каждой версией в нем появляются всё более лаконичные средства.

                                                                        На счет аналогии, выше, попытаюсь еще раз на пример. Вы знаете C# и думаю, знаете SQL и реляционные СУБД. Можно сказать, что SQL — это хоть и не обычный, но функциональный язык. Когда вы моделируете предметную область на шарпе, вы вынуждены строить кирпичик за кирпичиком, дописывая код и скрывая еще в классах, вовне только видимые интерфейсы. Каждую программу можно представить, как структуру в N-мерном пространстве. Где N — это количество переменных. Каждая ось — это переменная. В шарпе программист пытается кодом ограничить точки в пространстве, в которых может находиться программа. Он пытается снизить нагрузку на свое внимание и допустить меньше багов с помощью инкапсуляции. И в каждом методе и классе он оперирует небольшим набором объектов и прописывает пути в н-мерном пространстве. Далее на каждом новом уровне он скрывает код, который пишется на более низком уровне. В результате он получает код вверху, который оперирует небольшим количеством объектов и может делать небольшое ограниченное число действий. Надежда, что этого достаточно для покрытия требований.

                                                                        Что происходит в БД. Моделируются связи между данными и структуры данных декларативно. Уже после создания структуры БД смоделированы большинство зависимостей, которые не позволяют программе находиться в запрещенных точках в N-мерном пространстве. И есть мощный язык SQL, который позволяет почти без усилий делать любые преобразования, а структура БД следит за целостностью. Таким образом у нас с одной стороны нет скрытого поведения, с другой стороны затраты на изменения поведения программы минимальны.

                                                                        Т.е. проблемы решаются не скрыванием кода, а наличием ограничений и выразительного языка.
                                                                        Где-то так и в функциональном подходе. Вы создаете язык и ограничения, а потом пользуетесь сколько угодно гибко.
                                                                          0
                                                                          «Константа — это функция, которая не всегда возвращает одно и то же»!!!

                                                                          Ошибся. Править нельзя. Конечно, константа — функция, которая ВСЕГДА возвращает одно и то же
                                                                            0
                                                                            «Разница между кодом и данными условна.»
                                                                            Во многих современных системах — безусловна.

                                                                            «Функциональный подход проводит еще большее обобщение — всё функции, данных нет. „
                                                                            Вы что-то путаете. Как это нет данных? А операции над чем производятся?

                                                                            “Любая программа — это функция — преобразование входных параметров в выходные. Поэтому нет данных.»
                                                                            Есть. Входные и выходные параметры — это и есть данные.

                                                                            Всю вашу аналогию с БД и C# я вообще не понял, извините. (Отдельно замечу, что ваши замечания относительно БД неверны. Там есть и скрытое поведение, и безумные затраты на изменение поведения программы. Я с этим имею дело прямо сейчас.)

                                                                            Я, что характерно, попросил конкретный (часто встречающийся) пример, где было бы понятно, что такое «представление кода как данных» и чем это эффективнее, чем обычный код.
                                                                              0
                                                                              «Вы что-то путаете. Как это нет данных? А операции над чем производятся?»

                                                                              Над функциями. Ну, вот аналог на шарпе:

                                                                              delegate int IntConst();
                                                                              delegate IntConst IntOp(IntConst a, IntConst b);

                                                                              IntOp plus = (a, b) => () => a() + b();
                                                                              IntConst const6 = () => 6;
                                                                              IntConst const7 = () => 7;

                                                                              int r = plus(const6, const7)();

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

                                                                              «Там есть и скрытое поведение, и безумные затраты на изменение поведения программы»

                                                                              Это смотря кто как пишет. Обычно шарп программисты очень плохо к SQL относятся, потому что слабые в нем специалисты. А если еще плохо транзакт использовать и тригеры, то можно вообще написать что-то жуткое и страшное. То же и к шарпу относится. Если человек плохо на шарпе пишет, может написать очень плохой код. В реляционных БД есть свои и немалые плюсы, вот я описал, на что обращать внимание. Та же логика относится, например, к linq. Передача делегатов в методы позволяет создавать свои языки.
                                                                                0
                                                                                «Над функциями.»
                                                                                И пользователи тоже функции передают? И вы мне хотите сказать, что 6 и 7 — это не данные?

                                                                                «Это смотря кто как пишет.»
                                                                                Это верно для любого языка, поэтому не понятно, зачем приводить SQL в пример.

                                                                                «Передача делегатов в методы позволяет создавать свои языки. „
                                                                                Не позволяет. Передача делегатов в методы — это всего лишь передача делегатов в методы, тот или иной сценарий, тот или иной паттерн, если угодно. Нового языка из этого не появляется.
                                                                                  0
                                                                                  «И пользователи тоже функции передают? И вы мне хотите сказать, что 6 и 7 — это не данные?»

                                                                                  Нет )) Не данные. Пользователи передают функции-константы 6 и 7. Это формальность, правда? Функциональные языки обобщают понятие функции, в результате чего можно данные рассматривать как функции. В результате обобщения появляются замечательные возможности.

                                                                                  «Передача делегатов в методы — это всего лишь передача делегатов в методы, тот или иной сценарий, тот или иной паттерн, если угодно»

                                                                                  Смотря что вы называете языком. Создавая делегаты, которые берут на вход делегаты, вы создаете возможность потом создавать код, но отвечающий спецификациям делегатов, что вам позволит ограничить действия тех, кто будет использовать ваш код — т.е. создать язык, которым можно будет что-то выражать в какой-то предметной области. Яркий пример — методы для коллекций, берущие на вход лямбды. Вы коллекциями вращать можете как хотите и у вас почти SQL. Это уже язык, в каком-то смысле. Добавив еще парсер и абстрактное дерево — получили и новый синтаксис — linq
                                                                                    0
                                                                                    «Нет )) Не данные. Пользователи передают функции-константы 6 и 7. Это формальность, правда? „
                                                                                    Нет.

                                                                                    “Создавая делегаты, которые берут на вход делегаты, вы создаете возможность потом создавать код, но отвечающий спецификациям делегатов, что вам позволит ограничить действия тех, кто будет использовать ваш код — т.е. создать язык, которым можно будет что-то выражать в какой-то предметной области.»
                                                                                    Это не язык, а функциональность. Созданный для этой цели язык — это DSL.

                                                                                    «Это уже язык, в каком-то смысле.»
                                                                                    Нет. Как был C#, так и остался.
                                                                          0
                                                                          Беда только в том, что виртуальный вызов — это одна инструкция процессора, которая, еще и с предсказаниями дружит, в вот Dictionary — это как минимум поиск в хэш таблице. Согласитесь, это очень медленно, если вызывать такие функции *часто*
                                                                            0
                                                                            Сомневаюсь, что это беда. Важность оптимизации очень сильно переоценена.

                                                                            Еще не разу не сталкивался с критическими тормозами, потому что Dictionary использовал. Оптимизация — это процесс, который проводится после написания кода, который проходит в определенном порядке. Вначале замеры производительности, выбор узких мест, поиск оптимальных решений для узких мест.

                                                                            Узких мест почти всегда два-три. Где-то цикл, в нем цикл, а в нем самый внутренний цикл, где в сумме программа проводит почти всё время. Вот там и оптимизируют. И если код уже написан, а там замеры показали — тормозит Dictionarу, то нет проблем из него сделать switch.

                                                                            А так, просто, на вскидку, ни разу не помню, чтобы Dictionary был в чем-то виноват.
                                                                              0
                                                                              Это зависит от задачи. Лично я не могу себе позволить такие вещи писать в логике. В GUI — пожалуйста, в числодробилке — нет. А вообще, я к тому, что без накручивания лишних словарей ваша логика будет хотя и менее общей но быстрее и понятнее. А вот если ее будет не хватать, тогда уже…
                                                                        +1
                                                                        Полиморфизм «внутри» С++ ни на каких делегатах не работает. Он работает на указателях на таблицы виртуальных функций. Для каждого конкретного типа есть своя такая таблица. Когда вы создаете экземпляр виртуального класса, его конструктор инициализирует именно указатель на таблицу виртуальных функций, причем таблица эта статическая. Сама же таблица содержит указатели на собственно функции, которые надо вызывать при виртуальном вызове. И даже внутри таблицы это обычные указатели на функции, а никакие не делегаты. Делегат в с++ это std::function — объект с перегруженным оператором «скобочки».
                                                                          0
                                                                          Конечно. Я в курсе. Писал только о сути. А делегаты или указатели на функцию — это уже детали реализации.
                                                                          Делегаты — аналоги указателей на функции в большинстве случаев.
                                                                0
                                                                А как быть с унарными операциями в таком случае?
                                                                  0
                                                                  А как вы собираетесь вызывать унарные операции посредством метода PerformOperation(string, double, double)?

                                                                  (я к тому, что если метод другой, то под него и сигнатуры делегатов делаются другие, это не сложно)
                                                                  0
                                                                  А как добавлять новые операции в словарь из текста, введенного пользователем в рантайме?
                                                                    0
                                                                    Парсите выражение, делаете из него (в памяти) метод с сигнатурой (double, double) -> double, далее все аналогично.
                                                                      0
                                                                      >>делаете из него (в памяти) метод с сигнатурой (double, double) -> double

                                                                      Как это называется правильно? По каким словам гуглить?
                                                                  0
                                                                  _operations.ContainsKey("op") // тут видимо кавычки лишние.
                                                                  А также, что значит постфикс M в константах вида 1.0M?
                                                                  0
                                                                  Java-вариант, и без всяких делегатов. Имхо, ява попроще (в хорошем смысле) в этом случае.

                                                                  package tests;
                                                                  
                                                                  import java.util.HashMap;
                                                                  import java.util.Map;
                                                                  
                                                                  public class Calc {
                                                                      private Map<String, Operation> operations = new HashMap<>();
                                                                  
                                                                      static abstract class Operation {
                                                                          private String op;
                                                                  
                                                                          public Operation(String op) {
                                                                              this.op = op;
                                                                          }
                                                                  
                                                                          abstract int perform(int a, int b);
                                                                      }
                                                                  
                                                                      public static void main(String[] args) {
                                                                          Calc calc = new Calc()
                                                                                  .addOperation(new Operation("+") {
                                                                                      @Override
                                                                                      int perform(int a, int b) {
                                                                                          return a + b;
                                                                                      }
                                                                                  })
                                                                                  .addOperation(new Operation("*") {
                                                                                      @Override
                                                                                      int perform(int a, int b) {
                                                                                          return a * b;
                                                                                      }
                                                                                  });
                                                                  
                                                                          System.out.println(calc.perform("+", 2, 5));
                                                                          System.out.println(calc.perform("*", 2, 5));
                                                                      }
                                                                  
                                                                      private Calc addOperation(Operation operation) {
                                                                          operations.put(operation.op, operation);
                                                                          return this;
                                                                      }
                                                                  
                                                                      public int perform(String op, int a, int b) {
                                                                          return operations.get(op).perform(a, b);
                                                                      }
                                                                  }
                                                                  
                                                                    +2
                                                                    А вам не кажется, что код с делегатами компактнее и точнее выражает намерение? Потому что по сути ваше создание анонимного класса (я правильно читаю ваш код?) с одним методом — это и есть реализация делегатов в языке, который не умеет чистых (безклассовых) делегатов.

                                                                    Ну и да, и то, и другое — это реализация паттерна «стратегия», как уже говорилось выше.
                                                                      0
                                                                      Не кажется. Имхо, явный тип Operation кажется куда нагляднее загадочной сигнатуры Func<double, double, double>.
                                                                        +1
                                                                        В C# можно создать явный тип делегата Operation, причем можно с первой версии, Func появились позже, когда люди поняли, что каждый раз плодить новый тип — это несколько многословно.

                                                                        (более того, в посте пример с явным делегатом есть)
                                                                          0
                                                                          >это несколько многословно

                                                                          Зато более понятно. Хороший код должен быть удобен для чтения, в первую очередь, и лишь во вторую для написания. С написанием кода тоже проблем нет при текущем развитии IDE.
                                                                            0
                                                                            «Зато более понятно.»
                                                                            Это вопрос спорный и личный. Именно поэтому оставлены _оба_ варианта — можно создавать свои типы, можно пользоваться системными.

                                                                            Вашего примера касается не этот вопрос, а тот вопрос, что выразительнее — класс с одним методом или делегат (уникальный) с той же сигнатурой.
                                                                              –1
                                                                              Тема флеймообразующая, да. Тут java.sun.com/docs/white/delegates.html Санки давно объяснили почему они не станут делать делегаты для Java. Мне их аргументы весьма близки.

                                                                              А по поводу «оставлены _оба_ варианта», имхо, чем меньше вариантов — тем лучше — код однороднее выходит.
                                                                                +2
                                                                                «Мне их аргументы весьма близки.»
                                                                                Их аргументы сводятся к «у нас уже есть inner classes» и «это нарушает ООП». Первое, как мы понимаем, не аргумент, а второе можно понять, да. Но я уже давно считаю, что функциональное программирование ничем не хуже процедурного, и движение в его сторону в C# мне нравится.

                                                                                Но да, спорить за ФП я тут не хочу, очевидно.

                                                                                «чем меньше вариантов — тем лучше — код однороднее выходит. „
                                                                                И тем беднее (менее выразительно).

                                                                                Собственно, вы себе противоречите. Чтобы получить однородный код, идеально подходит обобщенный делегат Func (в вашем случае — обобщенный базовый класс Func), а наличие явных типов, соответствующих задаче — это уменьшение однородности кода и повышение его выразительности.
                                                                                  0
                                                                                  >И тем беднее (менее выразительно).

                                                                                  Глядите какой выразительный и богатый код:
                                                                                  www.cpan.org/misc/japh
                                                                                  ru.wikipedia.org/wiki/JAPH

                                                                                  Это к тому что богатство возможностей не всегда плюс.

                                                                                  >а наличие явных типов, соответствующих задаче — это уменьшение однородности кода и повышение его выразительности

                                                                                  Складывать яблоки с кирпичами? Нет уж, спасибо.

                                                                                    0
                                                                                    «Это к тому что богатство возможностей не всегда плюс.»
                                                                                    Все хорошо в меру.

                                                                                    Кстати, это действительно _выразительно_.

                                                                                    «Складывать яблоки с кирпичами? Нет уж, спасибо.»
                                                                                    Зато однородно.

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

                                                                                    Если что, апогей выразительности — DSL, который вообще не универсален (и не униформен).
                                                                                      0
                                                                                      Да. Я просто пытаюсь сказать, что по моему скромному мнению, однородность куда важнее выразительности (в стратегическом плане).
                                                                                        0
                                                                                        Но сами же протестуете против нее. предлагая вводить конкретный класс Operation вместо обобщенного Func.

                                                                                        Нелогичненько.

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

                                                                                          Это не я протестую. Это Вы неправильно трактуете смысл слова «однородность». По Вашей логике — чем больше классов в коде программы тем её код более неоднороден? Согласитесь, это абсурд. Наоборот, в яве мы имеем все те же plain old objects, а в шарпе имеем некую новую сущность под названием «делегат».
                                                                                            +1
                                                                                            «Наоборот, в яве мы имеем все те же plain old objects, а в шарпе имеем некую новую сущность под названием «делегат».»
                                                                                            Потому что в C# есть и ООП, и ФП.
                                                                                              0
                                                                                              Ну в яве тоже скоро будет FP (project lambda). Но даже там не будет никаких делегатов как отдельных сущностей. По сути, это будет просто краткая форма записи inner classes, т.е. тип лямбды будет равен SAM(Single Abstract Method)-типу на месте которого она была использована, хотя и с оптимизациями, основанными на invokedynamic. Хотя для прикладного программиста будет выглядеть и работать именно как сокращенная форма синтаксиса.
                                                                                                0
                                                                                                Вообще, delegate instance — это не отдельная сущность, это настоящий объект, являющийся ссылкой на метод конкретного класса.

                                                                                                В итоге, с точки зрения программиста (а, значит, с точки зрения абстракции) это будет одно и то же — in-place lambda expression.
                                                                                                  0
                                                                                                  >В итоге, с точки зрения программиста (а, значит, с точки зрения абстракции) это будет одно и то же

                                                                                                  Вот не уверен. В яве (то что будет в 8-ке)

                                                                                                  Comparator pointComparator = { Point p1, Point p2 -> p1.getX().compareTo(p2.getX()) };

                                                                                                  pointComparator будет типа Comparator и вставить его вместо, скажем, какого нибудь интерфейса DistanceFinder c той же сигнатурой вы не сможете.
                                                                                                  Что хорошо, т.к. повышает типобезопасность.
                                                                                                    +1
                                                                                                    Вы не поверите, но в C# строго то же самое: вставить делегат одного типа вместо делегата другого типа (за исключением правил приводимости) — нельзя. Иллюзия «нетипизированности» делегатов в C# возникает исключительно из-за того, что в C#, следуя трендам ФП, широко практикуется создание делегатов прямо на месте использования, без введения промежуточной переменной. Но поверьте, в принимающей стороне тип делегата указан явно и строго.
                                                                                                      0
                                                                                                      Да, признаю, с делегатами я ошибся.
                                                                                                      Но к Func<...> аргумент по-прежнему применим.

                                                                                                      using System;
                                                                                                      using System.Collections.Generic;
                                                                                                      using System.Linq;
                                                                                                      using System.Text;
                                                                                                      
                                                                                                      namespace ConsoleApplication1
                                                                                                      {
                                                                                                          class Program
                                                                                                          {
                                                                                                              static void Main(string[] args)
                                                                                                              {
                                                                                                                  var a = new A();
                                                                                                                  var b = new B();
                                                                                                      
                                                                                                                  var f1 = new A.PointComparator(b.compare);
                                                                                                                  var f2 = new A.PointDistanceFinder(b.compare);
                                                                                                        
                                                                                                                  Console.WriteLine(a.compare(f1, 2, 5));
                                                                                                                  Console.WriteLine(a.distance(f2, 2, 5));
                                                                                                                  
                                                                                                                  Func<int, int, int> f = b.compare; 
                                                                                                                  Console.WriteLine(a.compareF(f, 2, 5));
                                                                                                                  Console.WriteLine(a.distanceF(f, 2, 5));// <-- not correct use.
                                                                                                      
                                                                                                                  Console.Read();
                                                                                                              }
                                                                                                          }
                                                                                                      
                                                                                                          class B
                                                                                                          {
                                                                                                              public int compare(int a, int b)
                                                                                                              {
                                                                                                                  return a.CompareTo(b);
                                                                                                              }
                                                                                                          }
                                                                                                          class A
                                                                                                          {
                                                                                                              public delegate int PointComparator(int x, int y);
                                                                                                              public delegate int PointDistanceFinder(int x, int y);
                                                                                                      
                                                                                                              public int compare(PointComparator pointComparator, int x, int y)
                                                                                                              {
                                                                                                                  return pointComparator(x, y);
                                                                                                              }
                                                                                                      
                                                                                                              public int distance(PointDistanceFinder pointDistanceFinder, int x, int y)
                                                                                                              {
                                                                                                                  return pointDistanceFinder(x, y);
                                                                                                              }
                                                                                                      
                                                                                                              public int compareF(Func<int,int,int> pointComparator, int x, int y)
                                                                                                              {
                                                                                                                  return pointComparator(x, y);
                                                                                                              }
                                                                                                      
                                                                                                              public int distanceF(Func<int, int, int> pointDistanceFinder, int x, int y)
                                                                                                              {
                                                                                                                  return pointDistanceFinder(x, y);
                                                                                                              }
                                                                                                          }
                                                                                                      }
                                                                                                      
                                                                                                        0
                                                                                                        Ну так не пишите код с Func, кто же вам мешает?

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

                                                                                                        На хабре как-то была статья про dense code, это вот оно. Вам нравится sparse. Могу вас понять, но мне — не нравится. Для меня в нем слишком много избыточной информации.

                                                                                                        PS Обратите внимание, что вы в своем коде прекрасно создали distanceFinder из метода сравнения и никакая типизация вам не помешала.

                                                                                                        var f1 = new A.PointComparator(b.compare);
                                                                                                        var f2 = new A.PointDistanceFinder(b.compare);
                                                                    +1
                                                                    жесть, кол-во холиваров на квадратный сантиметр примерно как в каком-нибудь вбросе импортной шишки типа «почему Node.js — уг/рулит» :)
                                                                      0
                                                                      Ага, говорил про делегаты, а холивары про var'ы, стили программирования, етс. Народу только дай языком почесать. :)
                                                                      0
                                                                      Делегаты — это аналог Delphi'йских function of object, я правильно понял?
                                                                      А то как-то не доводилось писать на Си с диезом…
                                                                        +1
                                                                        делегаты — это типизированная ссылка на функцию, где функция это просто адрес с которого нужно начинать выполнения комманд.

                                                                        в Delphi function of object — это такая же ссылка на функцию. Мы не будем забывать, что архитектором C# был человек из команды Delphi: Anders Hejlsberg

                                                                        в C++ ссылки на функции — это просто ссылки на адрес в памяти, а в C# — существует строгая типизация.

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

                                                                        а в C# вы не сможете этого сделать. Получив при не соответствии типов InvalidCastException

                                                                          0
                                                                          Ну конечно, в языках типа Delphi и C++ есть reinterpret_cast, в Java и C# нет. Но это уже особенности среды выполнения: что она позволит и что нет. Значит, всё-таки аналог…