int Do(int x) => new Func<int, int>(y => {...}).Invoke(x);
S>Ну да, но это именно из серии "надо намеренно извратиться":)
А хотелось бы не извращаться, а чтобы компилятор просто поддерживал желаемый синтаксис.
Скажем, было:
private int Foo => ThirdPartyApi.ComputeValue();
И samius решил обмазаться using-expression'ом (в примере я его назвал with):
private int Foo => with (var sw = new StopwatchLogger()) ThirdPartyApi.ComputeValue();
Компилятор пусть просто генерит «IIFE-извращение»:
private int Foo => new Func<int>(() => { using (var sw = new StopwatchLogger()) return ThirdPartyApi.ComputeValue(); })();
или просто классический метод/свойство (не expression bodied).
S>Получаем правку expression trees, костыли ко всем linq-провайдерам и невозможность компиляции кода под младшие версии фреймворка.
Не получаем. Просто в expression tree вместо нашего выражения будет непрозрачный вызов метода.
Здравствуйте, Qbit86, Вы писали:
Q>Компилятор пусть просто генерит «IIFE-извращение»:
Ну... всё замечательно, но меня по-прежнему не отпускает мысль "но зачем???"
Что от такого изврата мы проигрываем — уже очевидно. Выигрыша нет вообще, если не считать двух сэкономленных скобок. Да и то они меняются на =>.
Не, серьёзно, это стоит того чтоб быть добавленным в язык вместо других нужных и действительно полезных вещей? Compiler team не резиновый, если они делают хотелку A, то в релиз автоматом не попадают B,C и D. Просто потому, что вместо полезной работы они искали обходы для всех дурацких последствий от A. Для всех желающих это в прямом эфире показывают. Что там с приятными мелочами типа params IEnumerable в шестом шаре?
S>>Получаем правку expression trees, костыли ко всем linq-провайдерам и невозможность компиляции кода под младшие версии фреймворка. Q>Не получаем. Просто в expression tree вместо нашего выражения будет непрозрачный вызов метода.
Тоже вариант, но это классический пример leaked abstraction: вместо относительно понятной ошибки компиляции или рантайма пользователь сообщает ошибку от провайдера "неподдерживаемый метод". Ну, или невалидный expression tree, как повезёт.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, samius, Вы писали:
S>>Expression Bodied Functions уже наступили на все грабли. И не я их предложил. То есть скобки и грабли с гайдлайнами не аргумент в случае Expression Bodied Functions, но почему-то аргумент в случае using...
S>Потому что exp bodied хороши для существующих выражений. А вот попытка добавить impure-выражения испортит и expression bodied тоже. Потому что смысл в них окончательно потеряется.
Не догоняю. Разве exp bodies запрещают impure-выражения? нет ведь. Все impure благополучно вставляются в exp bodies и ничего не портится. Или все что может быть испорчено, уже испорчено.
S>>Ненене, Дэвид Блейн! Не все что можно засунуть легально в expression проглотят linq-провайдеры и это не приводит к костылям. Поэтому не аргумент. S>А, ну т.е. ешё хуже решение. Теперь у нас есть выражения для expression-tree и выражения, которые не подходят для expression-tree и всё это ещё и различается для разных версий фреймворка. Как человек, писавший хоть и наколенный, но linq-провайдер — поверьте, там и без этого геммороя хватает
Наколенный и я писал. Выражения, которые не подходят для expression-tree linq-провайдера и без того есть.
Но я всеже не о выражениях ExpressionTree, а об выражениях, которые отличаются от стейтментов. Вот есть у нас стейтмент try/finally, и он ничего не ломает, ни в какие деревья выражений его совать не надо. Этот using стейтмент умеет возвращать результат. И это опять ничего не ломает (ведь существует, и никто не жалуется на это). И о ужас! Мы даже можем вставлять try/finally в лямбду!
Так вот, единственное, что сейчас отличает try/finally (using, lock) от выражения — это фактически пара внешних скобочек {}, которая придает стейтменту форму функции.
Очередное обновление языка нам позволяет записывать функции-мемберы без скобочек, но с =>, как у лямбд. Но вот то что в лямбды мы стейтменты вставлять можем, но если мы стейтменты вставим в функции-мемберы без скобочек, то сразу добавим геморроя — вот это я не понимаю.
S>>Expression Bodied Functions потребуют утверждения стандартов кодирования. На том же ровном месте. S>На сегодня нет. Т.к. ничего сложнее одного "?:" или linq-запроса туда не влазит. Т.е. правил достаточно тех же, что для любой одиночной строчки кода.
Туда не влазит, как я понимаю, ровно из-за того, что у стейтмента нет типа результата. Поправьте, если ошибаюсь.
S>>Работал на проектах и побольше. И мне не очевидно, почему такие проекты придется закапывать из-за using-expression. S>Не только из-за него. Когда у тебя на руках регулярно оказывается код, который надо поддерживать, но
S>1. Единственное, что с ним можно сделать — это сжечь и засыпать солью. S>2. Код даже трогать нельзя, т.к. он стоил несколько человеколет разработки, а код, который от него зависит — стоит эдак на порядок больше. S>3. Исправление нужно прямсейчасиписецкаксрочно
S>решения из разряда "лишь бы не как в гадлайнах" начинаешь очень не любить
Разве я предложил что-то ломать?
S>>Вопрос был изначально не в том, что бы на кого-то повесить головную боль, а в том, не приходило ли кому в голову, не обсуждалось ли? И только лишь. S>Обсуждалось конечно, ссылки на обсужденее более общей проблемы я в первом ответе и привёл. Вы там ещё написали "Я знаю, чем отличается выражение от стейтмента"
S>>pure и impure отлично смешиваются и без using-ов. да и никто не озаботился явной документацией pure и impure. S>Тем не менее на сегодняшний момент в шарпе надо специально постараться, чтоб запихнуть impure-код в выражения.
MethodCallExpression не отличит pure от impure.
S>>F# как раз заточен на работу в том же рантайме и том же фреймворке, что и C#. Кейворд use в частности заточен на работу с IDisposable, сценарии использования которого в точности совпадают со сценариями его использования на C#. S>Ну так и немерль и iron python заточены. Языками со своей концепцией и сценариями использования они от этого быть не перестали
Конечно нет, но работают они с тем же фреймворком и библиотеками, что и C#.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, Sinix:
Q>В expression bodied methods уже можно запихнуть statement'ы, завернув их в immediately-invoked function expression. Я так понимаю, samius'у будет достаточно сахара, не нужны хитрые expression trees.
Верно. Мне кажется что вообще у деревьев выражений и exp bodies ничего общего кроме набора букв "expression" нет. Т.е. body expression-ы мемберов никто в linq-провайдеры совать не собирается. Могу ошибаться, конечно.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Qbit86, Вы писали:
Q>>Компилятор пусть просто генерит «IIFE-извращение»:
S>Ну... всё замечательно, но меня по-прежнему не отпускает мысль "но зачем???" S>Что от такого изврата мы проигрываем — уже очевидно. Выигрыша нет вообще, если не считать двух сэкономленных скобок. Да и то они меняются на =>.
А какой выигрыш от exp bodies, кроме двух сэкономленных скобок, которые заменились на => ???
Здравствуйте, samius, Вы писали:
S>Не догоняю. Разве exp bodies запрещают impure-выражения? нет ведь. Все impure благополучно вставляются в exp bodies и ничего не портится. Или все что может быть испорчено, уже испорчено.
Не запрещает конечно. Но impure-выражений на сегодня как таковых нет. Разве что спрятанные в методы, инкремент и присваивания. Но последние два сразу видно. А от первого в принципе не защитишься без отсечения части сценариев типа кэширования.
Добавление using expression будет провоцировать написание кода с побочными эффектами (потому что коду без побочных эффектов юзинг по определению не нужен). Т.е. размывать и без того нечёткую разницу между выражениями и стейтментами
S>Вот есть у нас стейтмент try/finally, и он ничего не ломает, ни в какие деревья выражений его совать не надо. Этот using стейтмент умеет возвращать результат. И это опять ничего не ломает (ведь существует, и никто не жалуется на это). И о ужас! Мы даже можем вставлять try/finally в лямбду!
Неа, это уже будет не expression-лямбда, а сокращённая запись для анонимного делегата. Потому что будет содержать statement block. Ну и соответственно, не может быть сконвертирована в expression, по крайней мере для текущей версии шарпа.
Т.е. синтаксис для юзинга добавить можно, но мы получим парадоксальную ситуацию: код
int Do() => using (SomeScope()) SomeValue();
выглядит как expression lambda но ей не является т.к. его нельзя сконвертить в expression tree.
Т.е. это statement lambda, но тогда её надо записать как
int Do() => { using (SomeScope()) return SomeValue(); }
что ещё длиннее, чем оригинальный
int Do() { using (SomeScope()) return SomeValue(); }
В общем, борьба с синтаксическим оверхедом не задалась
Сорри, что сразу не привёл это объяснение, неправильно понял твой вопрос
S>Очередное обновление языка нам позволяет записывать функции-мемберы без скобочек, но с =>, как у лямбд. Но вот то что в лямбды мы стейтменты вставлять можем, но если мы стейтменты вставим в функции-мемберы без скобочек, то сразу добавим геморроя — вот это я не понимаю.
Потому что мы запутаем клиента: выглядит как expression lambda, но на самом деле это statement lambda.
Хотя, на самом деле мы тут страдаем фигнёй. Нужен коммент от ув. Никова.
S>Конечно нет, но работают они с тем же фреймворком и библиотеками, что и C#.
Фигово работают, надо признать. Ибо привычное API для этих языков плохо укладывается в рамки CLS.
Здравствуйте, samius, Вы писали:
S>А какой выигрыш от exp bodies, кроме двух сэкономленных скобок, которые заменились на => ???
Если выйти за пределы сценария "сахар для геттеров/простых методов" — никакого. Если не выходить, то поддержка statement as expression как раз не нужна. Почему — смотрим всё обсуждение, уже раза три наверно писал
Здравствуйте, Sinix, Вы писали:
S>Что от такого изврата мы проигрываем — уже очевидно.
От изврата, конечно, проигрываем. И выигрываем от небольшой поддержки компилятора, которая избавит от необходимости городить изврат.
S>Выигрыша нет вообще, если не считать двух сэкономленных скобок. Да и то они меняются на =>.
Ещё и «return ...»; выражению он не нужен. Но просто взять фигурные скобки можно только в expression-bodied-методах, заменив их на обычные. Есть и другие контексты, где может встретиться выражение. Например,
var foo = Foo(...);
var bar = ThirdPartyApi.ComputeBar(foo);
var qux = Qux(bar);
Скажем, хотим померять и залогировать время вызова сторонней функции:
var foo = Foo(...);
var bar = with (new StopwatchLogger()) ThirdPartyApi.ComputeBar(foo);
var qux = Qux(bar);
Тут уже добавление фигурных скобок будет ломать scope'ы.
S>Тоже вариант, но это классический пример leaked abstraction: вместо относительно понятной ошибки компиляции или рантайма пользователь сообщает ошибку от провайдера "неподдерживаемый метод". Ну, или невалидный expression tree, как повезёт.
Ещё раз: предложенный мной подход не порождает невалидных expression trees или новых недостижимых ранее ошибок linq-провайдеров. Есть понятная семантика, которая может быть достигнута существующими средствами языка и фреймворка; то есть не требуется ничего революционного кроме ложечки сахара.
Я не говорю, что жить не могу без using-выражений; эту ветку начал samius. Эти выражения я применял бы не каждый день, наверное; но значительно чаще, чем те linq-провайдеры, которыми не пользовался уже года три.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, samius, Вы писали:
S>>Не догоняю. Разве exp bodies запрещают impure-выражения? нет ведь. Все impure благополучно вставляются в exp bodies и ничего не портится. Или все что может быть испорчено, уже испорчено. S>Не запрещает конечно. Но impure-выражений на сегодня как таковых нет. Разве что спрятанные в методы, инкремент и присваивания. Но последние два сразу видно. А от первого в принципе не защитишься без отсечения части сценариев типа кэширования.
+1
S>Добавление using expression будет провоцировать написание кода с побочными эффектами (потому что коду без побочных эффектов юзинг по определению не нужен). Т.е. размывать и без того нечёткую разницу между выражениями и стейтментами
Здесь по поводу побочных эффектов согласен. Но! Тот C# в отличие от F# провоцирует на побочные эффекты практически все. Точнее, в F# есть хотя бы инфраструктура, которая может позволить в некоторых сценариях обходиться без них, а в C# у нас нет даже коллекций без побочных эффектов. Тем не менее, в F# try/finally — выражение, хотя для функционального языка такая провокация как серпом по яйцам. Но она есть.
S>>Вот есть у нас стейтмент try/finally, и он ничего не ломает, ни в какие деревья выражений его совать не надо. Этот using стейтмент умеет возвращать результат. И это опять ничего не ломает (ведь существует, и никто не жалуется на это). И о ужас! Мы даже можем вставлять try/finally в лямбду!
S>Неа, это уже будет не expression-лямбда, а сокращённая запись для анонимного делегата. Потому что будет содержать statement block. Ну и соответственно, не может быть сконвертирована в expression, по крайней мере для текущей версии шарпа.
S>Т.е. синтаксис для юзинга добавить можно, но мы получим парадоксальную ситуацию: код S>
S>int Do() => using (SomeScope()) SomeValue();
S>
S>выглядит как expression lambda но ей не является т.к. его нельзя сконвертить в expression tree.
Как же? можно. Речь таки о наличии лишней пары скобочек и оператора return. Т.е. мы говорим не о семантических отличиях, а о синтаксических и только лишь.
S>Т.е. это statement lambda, но тогда её надо записать как S>
S>int Do() => { using (SomeScope()) return SomeValue(); }
S>
S>что ещё длиннее, чем оригинальный
Это длинее чем оригинальный потому как using приходится оборачивать в скобки из-за того, что он стейтмент. Был бы он експрешном — не пришлось бы вставлять скобки. S>
S>int Do() { using (SomeScope()) return SomeValue(); }
S>
S>В общем, борьба с синтаксическим оверхедом не задалась
Конечно не задалась. Она не задалась еще с саамого expr bodies, т.к. вместо скобок пришлось писать =>. S>Сорри, что сразу не привёл это объяснение, неправильно понял твой вопрос
Не страшно.
S>>Очередное обновление языка нам позволяет записывать функции-мемберы без скобочек, но с =>, как у лямбд. Но вот то что в лямбды мы стейтменты вставлять можем, но если мы стейтменты вставим в функции-мемберы без скобочек, то сразу добавим геморроя — вот это я не понимаю. S>Потому что мы запутаем клиента: выглядит как expression lambda, но на самом деле это statement lambda.
И statement оно лишь потому что using не експрешн.
S>Хотя, на самом деле мы тут страдаем фигнёй. Нужен коммент от ув. Никова.
согласен, что фигней. Тем более, что в данном случае аргументы "почему нет" интересуют меня немного более самой фичи. Ведь речь всего лишь о скобочках, а не о просовывании чего-то нового в linq-провайдеры.
S>>Конечно нет, но работают они с тем же фреймворком и библиотеками, что и C#. S>Фигово работают, надо признать. Ибо привычное API для этих языков плохо укладывается в рамки CLS.
Есть предложения, как сделать лучше?
Здравствуйте, samius, Вы писали:
S>Интересно, никому не приходило в голову что Expression Bodied Functions and Properties хорошо бы дополнить возможностью использовать using statement в качестве expression?
S>тогда уж.
S>Иначе первый же залетевший дятел одно исключение — и утёкший ресурс
S>UPD. S>P.S. Кто сказал future монада? S>
S> public static Func<TReturn> Then<TValue, TReturn>(this Func<TValue> val, Func<TValue, TReturn> callback) { return () => callback(val()); }
S>...
S> Func<string> a = () => Console.ReadLine();
S> var b = a
S> .Then(_ => int.Parse(_))
S> .Then(_ => Math.Sqrt(_))
S> .Then(_ => { Console.WriteLine(_); return _; });
S> var c = b();
S>
Давно это было, вряд ли кто уж вспомнит.
Из F# спеки
Member
Description
member Using : 'a * ('a -> M<'b>) -> M<'b> when 'a :> IDisposable
Optional member. Used to de-sugar use bindings within computation expressions.
Construct
De-sugared Form
use pat = expr in cexpr
b.Using(expr, (fun pat -> cexpr))
use! pat = expr in cexpr
b.Bind(expr, (fun x -> b.Using(x, fun pat -> cexpr))
S>P.P.S. Все вопросы "НО ЗАЧЕМ???" — к топикстартеру. Это ему нужны были юзинги в выражениях
Я такого не утверждал (что нужны). Я поинтересовался, была ли такая идея?
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, samius, Вы писали:
AVK>>>Не? S>>Да, с учетом поправки Sinix, оно самое.
IT>Про where TValue : class, IDisposable не забудте. А то надиспозите.
Возражаю, не нужен такой констрейнт. Контракт на IDisposable предполагает возможность многократного вызова метода Dispose() и только. А флаги об успешном освобождении ресурса вовсе не обязательно держать в теле структуры.
Здравствуйте, Jack128, Вы писали:
J>как тут возможна утечка ?
Thread.Abort(), и, опционально, corrupted state exceptions, например out of memory при попытке выделить память под лямбду. Тут конечно ситуация из разряда "поздно пить боржоми", но мы ж инфраструктурный код обсуждаем, а не одноразовый хелпер.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Jack128, Вы писали:
J>>как тут возможна утечка ? S>Thread.Abort(), и, опционально, corrupted state exceptions, например out of memory при попытке выделить память под лямбду. Тут конечно ситуация из разряда "поздно пить боржоми", но мы ж инфраструктурный код обсуждаем, а не одноразовый хелпер.
Как страшно жить :-D