Здравствуйте, Sinix, Вы писали:
S>Roslyn team завела обсуждение на local functions (объявление функции внутри функции).
Отличная фича! Пожалуй самая часто используемая мной фича в Немерле.
S>Пока обсуждение чисто техническое, комментарии от c# team/vb team выложат пожже. У кого есть желание — участвуйте
S>
Зачем оно надо:
S>В принципе, особого смысла объявлять метод внутри метода нет.
Еще как есть!
S>В большинстве случаев можно положить private-метод рядом,
Локальная функция инкапсулирована внутри метода и не пересекается с другими. Приватные же методы, после некоторого количества, превращаются в лес.
Кроме того они позволяют делать замыкания (как лямбы) и могут быть безопасно оптимизированы (инлайнены).
S>если припёрло — S>
S>Func<int> x = ()=> { return 42; };
S>...
S>var y = x();
S>
S>и вперёд.
При этом создается делегат, который часто не нужен. Да и криво. Особо криво, если нужна рекурсия:
cs]
Func<int> x = null;
x = ()=> { return x(); };
...
var y = x();
S>Официальные комментарии такие:
S>1. Упрощает код — не всегда очевидно, что функция — просто хелпер для одного конкретного метода и вызывать её не по делу не надо.
Подтверждаю.
S>3. Перфоманс. Создание лямбды на каждый вызов, и, главное, отсутствие инлайнинга — не лучший способ добавить хелпер-код.
+1
S>4. Чтоб было совсем весело, в этого ужеежа запихнули фичи и от лямбд и от "обычных" методов: S>* local functions могут вести себя как лямбды S>* вывод типа для результата (если нет рекурсии)
В правильных языках вывод типов полный не зависящий от рекурсии. Рекурсия ничем не мешает выводу типов.
S>* рекурсия (если не используется вывод типа. и не говорите мне, что это тоже рекурсия).
Ничего не понял. Какие проблемы с рекурсией и выводе типов?
S>* захват переменных (aka lambda closures) S>* ref/out — параметры, named args, default arg values etc.
ref/out в принципе не нужны. Есть же замыкания. В прочем, больше не меньше.
S>Как-то так.
Все правильно. Но такое ощущение, что ты имеешь что-то против локальных функций?
ЗЫ
Чую, еще лет через 10 Шарпа таки станет Немерлом.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, tyomchick, Вы писали:
T>Здравствуйте, Sinix, Вы писали:
T>По-моему полезная вещь. T>После Delphi очень не хватало.
В Дельфи был бледный аналог. Реальный прототип — немрел и другие наследники ML-я. Там это реально удобнейшая штука.
T>Засорять тонной private хелперов напрягает, а лямбды выглядят слишком кучеряво.
+1
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Добавлю от себя, что это еще и отличный способ документировать код. Периодически выношу в локальные функции код который вызвается ровно один раз. Функция инлайнится и не создает проблем с производительность, зато появляется именованный кусок логики, который можно свернуть фолдингом.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IT, Вы писали:
IT>А мне очень не хватает локальной рекурсии, а вот локальный yield return ни разу не хотел, хотя использую его регулярно.
Локльный yield тоже приколен, но не для локальных функций, а для инлайн-итераторов (генераторов).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, xy012111, Вы писали:
X>Что ещё весьма полезно — это возможность иметь локальные дженерик-функции
По моему опыту они нужны очень редко. Немерл их поддерживает, но мы их почему-то почти не используем. Видимо потому, что все алгоритмы локальны и им ненужны дженирики.
Если понадобились дженерики, то с огромной вероятностью функцию имеет смысл вынести в бибилотечный класс.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Sinix, Вы писали:
S>Но помимо неё в язык по мере возможности добавляется мелочёвка, которая почти не влияет на public API и которая не особенно нужна в энтерпрайзе (серьёзно, если в коде такой баррдак, что даже приватные методы использовать небезопасно, ну сделай ты рефакторинг!).
Звучит так, что этот ваш энтерпрайз — это что-то очень плохое рассчитанное на участников специальных олимпиад.
S>Зато эта мелочёвка будет полезна для всяких скриптов/однодневных поделок и прочего write-only кода. S>Это ни хорошо, ни плохо, просто последствия попытки пролезть в мобильную нищу и в нишу инди-сайтов.
Практика показала, что локальные функции помогает очистить и структурировать код. Так что мимо.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Sinix, Вы писали:
S>Скорее всего с рослином проще дать генерик-функции, чем объяснять, почему нет.
На самом деле реализовать дженерик-локальнфе функции сложнее. Там есть подводные грабли.
S>UPD Как вариант, пригодится для скриптов и compiler as a service. Весь код запихиваем в одно тело метода и пусть оно себе компилируется, если сможет.
Зачем?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Ничего не понял. Какие проблемы с рекурсией и выводе типов?
Для лямбд вывод сделан, технических проблем показать ошибку для экзотики типа "X() { return X() — 1; }" тоже нет. Это только начало обсуждения — напиши им, может добавят.
VD>Все правильно. Но такое ощущение, что ты имеешь что-то против локальных функций?
Неа, есть — ну и фиг с ними. Другое дело, что в нормальном коде локальные функции особо не нужны. Если бардак дошёл до состояния "даже приватных методов не хватает для инкапсуляции" — надо код чинить, а не локальные функции прикручивать.
Но это, разумеется, только для моих сценариев использования. Для всякой тяжёлой инфраструктурщины типа внутренностей компилятора, уверен, всё наоборот
___
Чтоб не писать кучу ответов, тут же:
___
S>>Но помимо неё в язык по мере возможности добавляется мелочёвка, которая почти не влияет на public API и которая не особенно нужна в энтерпрайзе (серьёзно, если в коде такой баррдак, что даже приватные методы использовать небезопасно, ну сделай ты рефакторинг!). VD>Звучит так, что этот ваш энтерпрайз — это что-то очень плохое рассчитанное на участников специальных олимпиад.
Неа. Энтерпрайз — это когда код написанный один раз живёт минимум 5-10 лет и живёт не "один раз написано — всегда работает", а периодически меняется, потому что логика, пожелания заказчиков и законодательство не сочетаются практически никогда.
При этом объём этого кода (точнее, объём пожеланий заказчиков, которых не один-два мелких, а несколько сотен крупных) в принципе не позволяет повторить написанное силами одной команды (читай 5-10 человек) за разумный срок. А каждая строчка в биз-логике (только в ней, про инфраструктуру не говорим) — предметом долгих обсуждений и потенциальной причиной эпичных косяков у клиентов с вполне материальными последствиями. Типа увольнений и визита прокуратуры/налоговой из-за ошибки в коде восьмилетней давности. Т.е. код внезапно начинает иметь ценность.
Большинство "комнатных" подходов к разработке, для которых вполне справедливо "мы принимаем логичные решения", "этот код можно переписать", "всегда можно найти автора и узнать почему", "ошибку легко исправить" в таких условиях загнутся на втором-третьем году существования проекта. Да и команда разбежится нафиг, потому что никому не нравится копаться в мусорном коде.
Поэтому или пишем сразу короткий, понятный, поддерживаемый код, или проект очень быстро разрастается до состояния, когда внедрение даже самой малой фигни требует согласованной работы нескольких человек. Что сразу даёт 300% к срокам и -100 к морали команды.
VD>Практика показала, что локальные функции помогает очистить и структурировать код. Так что мимо.
Без обид, но нет. Скорее, получается копипаста и дублирование кода из-за того, что никто не знает, что подобный код уже написан. Разумеется, это справедливо только для проектов, в которых один человек в принципе не сможет охватить всю codebase.
Здравствуйте, VladD2, Вы писали:
VD>На самом деле реализовать дженерик-локальнфе функции сложнее. Там есть подводные грабли.
О! А можно подробности?
S>>UPD Как вариант, пригодится для скриптов и compiler as a service. Весь код запихиваем в одно тело метода и пусть оно себе компилируется, если сможет. VD>Зачем?
Ну а зачем в типовом скрипте что-то кроме локальных функций? Плюс, если вводить методы — клиентам надо будет прописывать сигнатуру основного метода.
Здравствуйте, Sinix, Вы писали:
VD>>Практика показала, что локальные функции помогает очистить и структурировать код. Так что мимо. S>Без обид, но нет. Скорее, получается копипаста и дублирование кода из-за того, что никто не знает, что подобный код уже написан. Разумеется, это справедливо только для проектов, в которых один человек в принципе не сможет охватить всю codebase.
В первых, о приватных методах тоже легко забыть и переопределить аналогичную логику в другом месте. Если же ты говоришь о тупой копипасте, когда локальную функцию копируют в другой метод, то тут просто разработчик не понимает, что общую логику нужно выносить в отдельный метод. А в случае с плохим разработчиком тебе приватные методы тоже не очень помогут (логика может понадобится в другом классе, например).
Зато локальные функции дают +10 к читаемости и поддерживаемости кода. Сравни:
public string DoStuff(string text, string text2)
{
if (ExtractInfo(text) == "HELLO") return ExtractInfo(text2);
...
}
public void A()
{
...
}
public void B()
{
...
}
private string ExtractInfo(string text)
{
...
}
и
public string DoStuff(string text, string text2)
{
var extractInfo = (s) => ....;
...
if (extractInfo(text) == "HELLO") return extractInfo(text2);
...
}
В первом случае тебе нужно перейти на определение приватного метода, потом вернуться обратно. Если функция сложнее, чем Substring, то делать это придётся несколько раз, чтобы удостовериться в корректности кода.
Локальная функция всегда рядом с местом использования, ты одновременно видишь как определение, так и применение.
Это одно из преимуществ. Другие тебе перечислили выше.
Имхо, любой, кто некоторое время писал на языке с нормальной поддержкой локальных функций, впоследствии будет по ним скучать в том же C#.
Здравствуйте, ionoy, Вы писали:
I>В первых, о приватных методах тоже легко забыть и переопределить аналогичную логику в другом месте.
Это постараться надо. Методы обычно называются логично, поиск студии или решарпера (который go to symbol, показывает подходящие методы в реалтайме) находит кандидатов на раз-два. Не, поддержка приватных методов тоже добавится со временем, но во-первых там больше шансов наткнуться на непонятное имя, во-вторых, смысл в разделении теряется окончательно.
I>Зато локальные функции дают +10 к читаемости и поддерживаемости кода. Сравни:
Ну тогда и сравнивать надо боль-менее реалистичное
public string DoStuff(string text, string text2)
{
if (ExtractInfo(text) == "HELLO") return ExtractInfo(text2);
...
if (ExtractInfo2(text) == "HELLO") return ExtractInfo2(text2);
...
}
private string ExtractInfo(string text)
{
...
...
...
}
private string ExtractInfo2(string text)
{
...
...
...
...
...
}
// иpublic string DoStuff(string text, string text2)
{
var extractInfo = (s) => ....;
{
...
...
...
}
var extractInfo2 = (s) => ....;
{
...
...
...
...
...
}
if (ExtractInfo(text) == "HELLO") return ExtractInfo(text2);
...
if (ExtractInfo2(text) == "HELLO") return ExtractInfo2(text2);
...
}
а не надуманный пример. Смысл растаскивать методы-локальные хелперы от метода, в котором они используются?
И в таком виде первый вариант выигрывает без вариантов. Т.е. первый же отрыв от сфероконины и переход к реальному коду — уже упс.
И да, для студии с peek definition вся эта возня с "в каком порядке расположено" особого смысла не имеет. Щёлкнул alt-f12 — увидел рядом реализацию, при необходимости поправил. Попробуйте, это одна из самых недооценённых фишек vs2013.
Здравствуйте, Sinix, Вы писали:
S>... а не надуманный пример. Смысл растаскивать методы-локальные хелперы от метода, в котором они используются? S>И в таком виде первый вариант выигрывает без вариантов. Т.е. первый же отрыв от сфероконины и переход к реальному коду — уже упс.
Спорить не буду, сам всё увидишь, если всё-таки добавят.
S>И да, для студии с peek definition вся эта возня с "в каком порядке расположено" особого смысла не имеет. Щёлкнул alt-f12 — увидел рядом реализацию, при необходимости поправил. Попробуйте, это одна из самых недооценённых фишек vs2013.
Это да, никак не могу заставить себя ей пользоваться, несмотря на явную полезность. Но чтобы подправить код, в любом случае придётся перемещаться.
Здравствуйте, Sinix, Вы писали:
S>Это постараться надо. Методы обычно называются логично, поиск студии или решарпера (который go to symbol, показывает подходящие методы в реалтайме) находит кандидатов на раз-два. Не, поддержка приватных методов тоже добавится со временем, но во-первых там больше шансов наткнуться на непонятное имя, во-вторых, смысл в разделении теряется окончательно. S>а не надуманный пример. Смысл растаскивать методы-локальные хелперы от метода, в котором они используются? S>И в таком виде первый вариант выигрывает без вариантов. Т.е. первый же отрыв от сфероконины и переход к реальному коду — уже упс.
Локальные функции — это вопрос скоупа. Просто найди все обычные рекомендации из C++/C# о том, что надо объявляеть локальные переменные ближе к их использованию, и зачем ограничивать их области видимости. А потом примени эти рекомендации к локальным функциям. Выносить локальные функции в скоуп класса так же неприятно, как выносить локальные переменные алгоритма в поля класса.
S>И да, для студии с peek definition вся эта возня с "в каком порядке расположено" особого смысла не имеет. Щёлкнул alt-f12 — увидел рядом реализацию, при необходимости поправил. Попробуйте, это одна из самых недооценённых фишек vs2013.
Здравствуйте, ionoy, Вы писали:
I>Спорить не буду, сам всё увидишь, если всё-таки добавят.
Ок
S>>И да, для студии с peek definition вся эта возня с "в каком порядке расположено" особого смысла не имеет. I>Это да, никак не могу заставить себя ей пользоваться, несмотря на явную полезность. Но чтобы подправить код, в любом случае придётся перемещаться.
Неа, правится прямо в peek window, в том-то и прелесть.
Здравствуйте, Qbit86, Вы писали:
Q>Локальные функции — это вопрос скоупа. Просто найди все обычные рекомендации из C++/C# о том, что надо объявляеть локальные переменные ближе к их использованию, и зачем ограничивать их области видимости. А потом примени эти рекомендации к локальным функциям.
Дело в том, что у меня есть опыт использования чего-то похожего на практике. Причём в случае, где без делегатов/локальных функций особо не обойтись. Штука с кучей магии под капотом, что-то типа динамически генерируемого КА с автоматическим разруливанием циклов/зависимостей. Казалось бы, идеальный пример для local functions.
Аля
void Handle(Something y)
{
Rule(x => x.Sum > 100, x => { x.Sum = 100; });
IfChanged(x => x.Sum, x => { x.WorkLimit = WorkFromScale(x.Sum); });
IfChanged(x => x.WorkLimit, x => FixWorked(x));
IfChanged(x => x.Worked, x => FixSum(x));
Run(y);
}
Так вот, несмотря на то, что в принципе все эти WorkFromScale(), FixWorked(), FixSum() etc состояли из одной-двух строчек, на практике никто и никогда добровольно не записывал их прямо по месту использования. Хотя хелпер для этого был предусмотрен.
Потому что с точки зрения логики конкретное действие и настройка workflow — это принципиально разные вещи. И нефиг их смешивать в один метод.
Q>Выносить локальные функции в скоуп класса так же неприятно, как выносить локальные переменные алгоритма в поля класса.
Не, фигня получается. В результате в одном методе смешиваются две ответственности: реализация и собственно логика в виде наборов вызовов этой самой реализации. Ещё и с кучей неявных зависимостей из-за замыканий.
Ну, т.е. мы вернулись к ситуации, от которой пытались уйти рефакторингом на отдельные методы
Q>В VSCode аналогичная фича тоже приятно реализована.
+1.
Здравствуйте, Sinix, Вы писали:
S>Roslyn team завела обсуждение на local functions (объявление функции внутри функции).
Это какие замыкания в C# по счёту? Третьи?
S>Официальные комментарии такие: S>1. Упрощает код — не всегда очевидно, что функция — просто хелпер для одного конкретного метода и вызывать её не по делу не надо.
Для этого достаточно лямбды.
S>2. лямбды не умеют в yield return. Т.е. самый простой с валидацией аргументов превращается в
В чём проблема добавить?
S>3. Перфоманс. Создание лямбды на каждый вызов, и, главное, отсутствие инлайнинга — не лучший способ добавить хелпер-код.
Казалось бы — почему бы не оптимизировать лямбды?
S>4. Чтоб было совсем весело, в этого ужеежа запихнули фичи и от лямбд и от "обычных" методов: S>* local functions могут вести себя как лямбды
В каком смысле?
S>* вывод типа для результата (если нет рекурсии)
Лямбды тоже могли бы это уметь.
S>* рекурсия
Пожалуй единственный аргумент за.
S>* захват переменных (aka lambda closures)
Это и так умеет лямбда.
S>* ref/out — параметры, named args, default arg values etc.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Это какие замыкания в C# по счёту? Третьи? EP>Это и так умеет лямбда.
Я бы вместо добавления локальных функций урезал лямбды — запретил им быть замыканиями и содержать больше одного выражения.
С целью слегка ограничить полет фантазии особо одаренных.
Правда, уверен, в текущем обсуждении меня мало кто поддержит.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
S>>Roslyn team завела обсуждение на local functions (объявление функции внутри функции). EP>Это какие замыкания в C# по счёту? Третьи?
Угу. Лямбды, итераторы/await, локальные функции. И, для ценителей, CreateDelegate + firstArgument:
public static void Do(string a, int b)
{
for (int i = 0; i < b; i++)
{
Console.WriteLine(a);
}
}
static void Main()
{
Action<int> a = (Action<int>)Delegate.CreateDelegate(
typeof(Action<int>),
"hello",
typeof(Program).GetMethod("Do"));
a(10);
Console.Write("Done.");
Console.ReadKey();
}
S>>1. Упрощает код — не всегда очевидно, что функция — просто хелпер для одного конкретного метода и вызывать её не по делу не надо. EP>Для этого достаточно лямбды.
Если метод не вызывается миллионы раз. Каждый вызов — аллокация (если есть замыкания) + нет инлайнинга (в любом случае).
S>>2. лямбды не умеют в yield return. Т.е. самый простой с валидацией аргументов превращается в EP>В чём проблема добавить?
S>>3. Перфоманс. Создание лямбды на каждый вызов, и, главное, отсутствие инлайнинга — не лучший способ добавить хелпер-код. EP>Казалось бы — почему бы не оптимизировать лямбды?
Потому что для делегатов в принципе инлайнинг не сделаешь. Разве что реврайтом при компиляции, но это по общим затратам сложнее, чем добавить local functions
S>>4. Чтоб было совсем весело, в этого ужеежа запихнули фичи и от лямбд и от "обычных" методов: S>>* local functions могут вести себя как лямбды EP>В каком смысле?
Следы редактирования Лишний пункт
S>>* рекурсия EP>Пожалуй единственный аргумент за.
Неа, уже приводили вариант с рекурсией и делегатами. Тут нет одного конкретного сильного аргумента, только в комплексе перевешивает.
В общем добавят — и фиг с ним. Обсуждение развели, как будто фича века