[Ann, c#7] local functions
От: Sinix  
Дата: 20.05.15 07:18
Оценка: 285 (12) +3
Roslyn team завела обсуждение на local functions (объявление функции внутри функции).
Пока обсуждение чисто техническое, комментарии от c# team/vb team выложат пожже. У кого есть желание — участвуйте

Зачем оно надо:


В принципе, особого смысла объявлять метод внутри метода нет. В большинстве случаев можно положить private-метод рядом, если припёрло —
Func<int> x = ()=> { return 42; };
...
var y = x();

и вперёд.

Официальные комментарии такие:

1. Упрощает код — не всегда очевидно, что функция — просто хелпер для одного конкретного метода и вызывать её не по делу не надо.
2. лямбды не умеют в yield return. Т.е. самый простой с валидацией аргументов превращается в
public IEnumerable<char> GetChars(int count)
{
  if (count < 0) throw something;

  return GetCharsCore(count);
}

private IEnumerable<char> GetCharsCore(int count)
{
  for (int i = 0; i<count; i++) yield return 'a';
}

вместо
public IEnumerable<char> GetChars(int count)
{
  IEnumerable<char> GetCharsCore(int count)
  {
    for (int i = 0; i<count; i++) yield return 'a';
  }

  if (count < 0) throw something;

  return GetCharsCore(count);
}


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

4. Чтоб было совсем весело, в этого ужеежа запихнули фичи и от лямбд и от "обычных" методов:
* local functions могут вести себя как лямбды
* вывод типа для результата (если нет рекурсии)
* рекурсия (если не используется вывод типа. и не говорите мне, что это тоже рекурсия).
* захват переменных (aka lambda closures)
* ref/out — параметры, named args, default arg values etc.

Как-то так.
Re: [Ann, c#7] local functions
От: tyomchick Россия  
Дата: 20.05.15 07:53
Оценка: +2 -1
Здравствуйте, Sinix, Вы писали:

По-моему полезная вещь.
После Delphi очень не хватало.
Засорять тонной private хелперов напрягает, а лямбды выглядят слишком кучеряво.
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Re: [Ann, c#7] local functions
От: hi_octane Беларусь  
Дата: 20.05.15 08:18
Оценка: 24 (3) +5
S>В принципе, особого смысла объявлять метод внутри метода нет. В большинстве случаев можно положить private-метод рядом, если припёрло.

В Nemerle постоянно пользовался локальными функциями для всяких рекурсивных алгоритмов, работающих вместе подписчиков на события, и просто сложных методов (удобно сделать один набор проверок входных аргументов и в локальных функциях делать исключительно работу). Так что их добавление дело правильное.
Re[2]: [Ann, c#7] Local functions
От: Qbit86 Кипр
Дата: 20.05.15 08:21
Оценка: +1
Здравствуйте, hi_octane, Вы писали:

_>В Nemerle постоянно пользовался локальными функциями для всяких рекурсивных алгоритмов


В Скале тоже пользовался для тех же целей.
Глаза у меня добрые, но рубашка — смирительная!
Re: [Ann, c#7] local functions
От: IT Россия linq2db.com
Дата: 20.05.15 14:04
Оценка: 44 (3) +5
Здравствуйте, Sinix, Вы писали:

S>Roslyn team завела обсуждение на local functions (объявление функции внутри функции).


Ну наконец-то.

S>

Зачем оно надо:


Альтернатив локальной функции две: лямбды и приватные хелперы. Лямбды в данном контексте инородны, выглядят криво, особенно рекурсивные, и не отличаются высокой производительностью. Приватные хелперы требуют передачи всего необходимого контекста, что легко порождает монстриков с количеством параметров 5+. К тому же приватные хелперы подталкивают неокрепшие умы к их повторному использованию (из разных методов). Тому повторному использованию, которое не только добро, но ещё и абсолютное, незамутнённое неосветлённое ни единой каплей добра, зло.
Если нам не помогут, то мы тоже никого не пощадим.
Отредактировано 20.05.2015 19:37 IT . Предыдущая версия . Еще …
Отредактировано 20.05.2015 14:15 IT . Предыдущая версия .
Re[2]: [Ann, c#7] local functions
От: Neco  
Дата: 20.05.15 18:09
Оценка:
Здравствуйте, IT, Вы писали:

IT> К тому же приватные хелперы подталкивают неокрепшие умы к их повторному использованию (из разных методов). Тому повторному использованию, которое не только добро, но ещё и абсолютное, незамутнённое ни единой каплей добра, зло.

а можно пример такого зла?
p.s. в лямбдах очень не хватает yield return, рекурсию в лямбдах ни разу не хотел. ну а так в целом всегда хотелось, конечно, чтобы класс не "засорять".
всю ночь не ем, весь день не сплю — устаю
Re[2]: [Ann, c#7] local functions
От: AlexRK  
Дата: 20.05.15 18:54
Оценка: +1 -1
Здравствуйте, IT, Вы писали:

IT>Приватные хелперы требуют передачи всего необходимого контекста, что легко порождает монстриков с количеством параметров 5+.


У локальных функций свои беды. Если они являются одновременно замыканиями, то могут возникнуть коллизии с перекрытыми названиями параметров и внешних переменных (или параметров корневого метода), что может способствовать снижению читабельности. Да и вообще функция среди кода выглядит не очень. А локальные функции внутри локальных функций — это еще круче.
Понятно, что абузить можно любой механизм, но, ИМХО, локальные функции способствуют злоупотреблениям (из дельфийского опыта).
Re[3]: [Ann, c#7] local functions
От: IT Россия linq2db.com
Дата: 20.05.15 19:15
Оценка: 1 (1) +2
Здравствуйте, AlexRK, Вы писали:

ARK>Понятно, что абузить можно любой механизм, но, ИМХО, локальные функции способствуют злоупотреблениям (из дельфийского опыта).


Паскалевский опыт использования локальных функций у меня достаточно нейтральный. Т.е. ни плохо, ни хорошо. Нормальная удобная фича. Зато Немерловый опыт самый положительный.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: [Ann, c#7] local functions
От: IT Россия linq2db.com
Дата: 20.05.15 19:34
Оценка: 2 (1) +1
Здравствуйте, Neco, Вы писали:

N>а можно пример такого зла?


// Безобидное начало.
class A1
{
    public void Method1()
    {
        var n = Helper("123");
    }

    private int Helper(string str)
    {
        var s = str + "456";

        return s.Length;
    }
}

// Эволюция кода I. Появляется ещё один шарильщик хелпера.
class A2
{
    public void Method1()
    {
        var n = Helper("123", false);
    }

    public void Method2()
    {
        var n = Helper("890", true);
    }

    private int Helper(string str, bool flag)
    {
        var s = str + (str.StartsWith("1") ? "456" : "654");

        if (flag == true)
            s = s.Substring(1);

        return s.Length;
    }
}

// Эволюция кода II. Появляется третий шарильщик хелпера.
class A3
{
    public void Method1()
    {
        var n = Helper("123", false, false, null);
    }

    public void Method2()
    {
        var n = Helper("890", true, false, null);
    }

    public void Method3()
    {
        var n = Helper("654", true, true, 3);
    }

    private int Helper(string str, bool flag1, bool flag2, int? p)
    {
        var s = str + (str.StartsWith("1") ? "456" : "654");

        if (flag2 == true)
        {
            if (flag1 == true)
                s = s.Substring(1);
        }
        else
        {
            if (flag1 == true || p == null)
                s = s.Substring(2);
            else
                s = s.Substring(p.Value);
        }

        return s.Length;
    }
}

// Код без эволюции, т.е. то, что получилось бы без использования повторного использования.
class A4
{
    public void Method1()
    {
        var n = 6;
    }

    public void Method2()
    {
        var n = 5;
    }

    public void Method3()
    {
        var n = 3;
    }
}


N>p.s. в лямбдах очень не хватает yield return, рекурсию в лямбдах ни разу не хотел.


А мне очень не хватает локальной рекурсии, а вот локальный yield return ни разу не хотел, хотя использую его регулярно.
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: [Ann, c#7] local functions
От: M3fN  
Дата: 21.05.15 00:15
Оценка:
Здравствуйте, IT, Вы писали:

IT>А мне очень не хватает локальной рекурсии...


Вы об этом?

    EventHandler initializationCompleted = null;
    EventHandler initializationCompletedCl = delegate(object sender, EventArgs args)
    {
        // например
        wrapper.InitializationCompleted -= initializationCompleted;
        //...

        initializationCompleted (sender, args);
    };
    initializationCompleted = initializationCompletedCl;
    wrapper.InitializationCompleted += initializationCompleted;
Re: [Ann, c#7] local functions
От: xy012111  
Дата: 21.05.15 09:39
Оценка: 1 (1) +1
Здравствуйте, Sinix, Вы писали:

S>Roslyn team завела обсуждение на local functions (объявление функции внутри функции).

S>Пока обсуждение чисто техническое, комментарии от c# team/vb team выложат пожже. У кого есть желание — участвуйте

S>

Зачем оно надо:


Что ещё весьма полезно — это возможность иметь локальные дженерик-функции
Re[2]: [Ann, c#7] local functions
От: hardcase Пират http://nemerle.org
Дата: 21.05.15 11:06
Оценка:
Здравствуйте, xy012111, Вы писали:

X>Что ещё весьма полезно — это возможность иметь локальные дженерик-функции


Зачем?
/* иЗвиНите зА неРовнЫй поЧерК */
Отредактировано 21.05.2015 11:06 hardcase . Предыдущая версия .
Re[3]: [Ann, c#7] local functions
От: Sinix  
Дата: 21.05.15 11:44
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Зачем?

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

Классика "дизайним очередной big thing и вылизываем всё, что с ним связано" никуда не делась.
Но помимо неё в язык по мере возможности добавляется мелочёвка, которая почти не влияет на public API и которая не особенно нужна в энтерпрайзе (серьёзно, если в коде такой баррдак, что даже приватные методы использовать небезопасно, ну сделай ты рефакторинг!).

Зато эта мелочёвка будет полезна для всяких скриптов/однодневных поделок и прочего write-only кода.
Это ни хорошо, ни плохо, просто последствия попытки пролезть в мобильную нищу и в нишу инди-сайтов.
Re[4]: [Ann, c#7] local functions
От: hardcase Пират http://nemerle.org
Дата: 21.05.15 12:20
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Зато эта мелочёвка будет полезна для всяких скриптов/однодневных поделок и прочего write-only кода.


За много лет написания всякого, в т.ч. и write-only кода, на языке, умеющем локальные функции, мне наверно один или два раза потребовалось иметь локальные generic функции. Речь именно о функциях в которых параметры типов описываются явно, а не наследуются от метода в который вложены.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[5]: [Ann, c#7] local functions
От: Sinix  
Дата: 21.05.15 12:31
Оценка:
Здравствуйте, hardcase, Вы писали:

H>За много лет написания всякого, в т.ч. и write-only кода, на языке, умеющем локальные функции, мне наверно один или два раза потребовалось иметь локальные generic функции. Речь именно о функциях в которых параметры типов описываются явно, а не наследуются от метода в который вложены.


Скорее всего с рослином проще дать генерик-функции, чем объяснять, почему нет.

UPD Как вариант, пригодится для скриптов и compiler as a service. Весь код запихиваем в одно тело метода и пусть оно себе компилируется, если сможет.
Отредактировано 21.05.2015 12:36 Sinix . Предыдущая версия .
Re[6]: [Ann, c#7] local functions
От: hardcase Пират http://nemerle.org
Дата: 21.05.15 12:39
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Скорее всего с рослином проще дать генерик-функции, чем объяснять, почему нет.


Под капотом поддержку генериков делать придется хотя бы потому, что такие параметры наследуются от метода, в который вложена локальная функция. Расширить сигнатуру до поддержки собственных генерик-параметров у локальной функции не трудно. Я говорю о том, что такого рода функции мало востребованы в реальной жизни.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[7]: [Ann, c#7] local functions
От: Sinix  
Дата: 21.05.15 13:35
Оценка:
Здравствуйте, hardcase, Вы писали:

S>>Скорее всего с рослином проще дать генерик-функции, чем объяснять, почему нет.


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

Там не так всё будет.
Для самого замыкания всё уже сделано.

А вот local generic-функция просто переедет в класс замыкания,
void A<T>(T x)
{
  T2 DoSmth<T2>() { return SomeCode<T2>(x)};

  DoSmth<int>();
}

// =>
class Closure<T>
{
   public T _x
   public T2 DoSmth<T2>() { return SomeCode<T2>(_x)};
}
// ...
void A<T>(T x)
{
  var xyz = new Closure<T> { _x = x };
  xyz.DoSmth<int>();
}


И вот объяснить, почему при переезде "генерики низзя" будет куда сложнее, чем собственно перенести.
Это ж надо задокументировать в стандарте, добавить диагностики/ошибки компилятора, добавить quick fix в студию, покрыть всё это дело тестами, написать на всё перечисленное документацию, пару статей и несколько глав в книжке по MS cert exam и тд и тп.

Лучше чем-то полезным заняться
Re[4]: [Ann, c#7] local functions
От: AngeL B. Россия  
Дата: 21.05.15 14:36
Оценка: +2
Здравствуйте, IT, Вы писали:

IT>Паскалевский опыт использования локальных функций у меня достаточно нейтральный. Т.е. ни плохо, ни хорошо. Нормальная удобная фича. Зато Немерловый опыт самый положительный.


Для того чтобы опыт использования локальных функций был однозначно положительным, крайне желательно, чтобы язык поддерживал оптимизацию хвостовых рекурсий. Иначе опыт, приобретенный в Немерле/Скале/ФШарпе, может сыграть плохую службу. Вроде локальные функции есть, но нормальную рекурсию на них не напишешь.
Re[5]: [Ann, c#7] local functions
От: IT Россия linq2db.com
Дата: 21.05.15 19:14
Оценка:
Здравствуйте, AngeL B., Вы писали:

AB>Для того чтобы опыт использования локальных функций был однозначно положительным, крайне желательно, чтобы язык поддерживал оптимизацию хвостовых рекурсий. Иначе опыт, приобретенный в Немерле/Скале/ФШарпе, может сыграть плохую службу. Вроде локальные функции есть, но нормальную рекурсию на них не напишешь.


В Немерле с этим всё в порядке.
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: [Ann, c#7] local functions
От: IT Россия linq2db.com
Дата: 21.05.15 19:23
Оценка: +2
Здравствуйте, M3fN, Вы писали:

IT>>А мне очень не хватает локальной рекурсии...

MN>Вы об этом?

Зачем так сложно?

    Func<int,int> func = null;

    func = n =>
    {
        ....
        func(n + 1);
        ....
    };

    func(0);


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