Здравствуйте, Воронков Василий, Вы писали:
ВВ>А что с ленивостью? То, что дает итератор в этом плане, прекрасно реализуется опять же без итератора через ФВП.
Вопрос в другую сторону был поставлен. Почему в Haskell не нужны итераторы. Ответ — потому что ленивость уже решает эти задачи. Почему нужны в C# — может потому что так удобнее?
Здравствуйте, lomeo, Вы писали:
L>Вопрос в другую сторону был поставлен. Почему в Haskell не нужны итераторы. Ответ — потому что ленивость уже решает эти задачи. Почему нужны в C# — может потому что так удобнее?
У меня вообще схожие впечатления. Ленивость, необязательно неявная, если требуется non strict поведение — как при создании контейнеров, так и при вычислениях. И стандартные паттерны ФП для всего остального.
Т.е. при таком раскладе сама по себе функция-генератор оказывается как бы невостребованной.
Ну и для строгих функциональных языков родственное итератору понятие — перечисление тоже весьма полезно как средство
обобщенно представлять разные виды последовательностей (конечные и бесконечные, строгие и ленивые) работать с ними
однородно используя тот же набор ФВП, что и для списков и плюс изолировать в них грязь, как пример BatEnum для
OCaml — http://thelema.github.com/batteries-included/hdoc/BatEnum.html
Здравствуйте, FR, Вы писали:
FR>Ну и для строгих функциональных языков родственное итератору понятие — перечисление тоже весьма полезно как средство FR>обобщенно представлять разные виды последовательностей (конечные и бесконечные, строгие и ленивые) работать с ними FR>однородно используя тот же набор ФВП, что и для списков и плюс изолировать в них грязь, как пример BatEnum для FR>OCaml — http://thelema.github.com/batteries-included/hdoc/BatEnum.html
Да, вот только "перечислитель" в стиле IEnumerator, подобный которому предлагается и в BatEnum, как я понимаю, это достаточно императивная по своей природе штука. Он предлагает обобщенный способ для работы с коллекциями, но *какой* способ? Скажем, вместо красивого рекурсивного:
let fold f z x::xs = fold f (f z x) xs
| f z [] = z
Я что буду писать? Это если мне захочется сделать свою функцию, которая что-то делает с энумератором.
Альтернативой тут может быть подход к абстракции для контейнера с другого конца — вот тот же Foldable.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Да, вот только "перечислитель" в стиле IEnumerator, подобный которому предлагается и в BatEnum, как я понимаю, это достаточно императивная по своей природе штука.
Да галимый императив в реализации.
Но интерфейс практически чисто функциональный.
ВВ>Он предлагает обобщенный способ для работы с коллекциями, но *какой* способ?
Очень простой работать через базовые ФВП.
ВВ>Скажем, вместо красивого рекурсивного:
ВВ>
ВВ>let fold f z x::xs = fold f (f z x) xs
ВВ> | f z [] = z
ВВ>
ВВ>Я что буду писать? Это если мне захочется сделать свою функцию, которая что-то делает с энумератором.
В случае fold ничего ни будешь, он уже есть, также как и все базовые ФВП.
В этом-то и суть что есть база в виде ФВП и все остальное выражается через нее. Плюс list comprehensions в
батарейках по умолчанию дает Enum.
ВВ>Альтернативой тут может быть подход к абстракции для контейнера с другого конца — вот тот же Foldable.
Здравствуйте, FR, Вы писали:
FR>Да галимый императив в реализации. FR>Но интерфейс практически чисто функциональный.
Интерфейс — да. Я имею в виду — а как мне писать код, если я решу написать свою функцию, какой-нибудь особенный "fold", работающий с этим самым энумератором. Как я понимаю, через циклы или что-то в этом роде?
FR>В случае fold ничего ни будешь, он уже есть, также как и все базовые ФВП. FR>В этом-то и суть что есть база в виде ФВП и все остальное выражается через нее.
Получается, что это функциональная обвертка над императивной штукой
FR>Плюс list comprehensions в батарейках по умолчанию дает Enum.
Т.е. list comprehension фактически становится enum comprehension?
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Интерфейс — да. Я имею в виду — а как мне писать код, если я решу написать свою функцию, какой-нибудь особенный "fold", работающий с этим самым энумератором. Как я понимаю, через циклы или что-то в этом роде?
Зачем, кошерный способ писать используя базовые ФВП.
Если это не подходит есть низкоуровневые http://thelema.github.com/batteries-included/hdoc/BatEnum.html#6_Usefulfunctions
функции, конечно они по сути императивы, но циклов и изменяемых переменных для работы не требуют.
FR>>В случае fold ничего ни будешь, он уже есть, также как и все базовые ФВП. FR>>В этом-то и суть что есть база в виде ФВП и все остальное выражается через нее.
ВВ>Получается, что это функциональная обвертка над императивной штукой
Да.
FR>>Плюс list comprehensions в батарейках по умолчанию дает Enum.
ВВ>Т.е. list comprehension фактически становится enum comprehension?
Ну там возможно задавать что получишь на выходе, по умолчанию да enum, но
можно и списки и даже строки и массивы:
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.
В энергичных языках есть важная разница: кто кого вызывает и контролирует — основной код просит у итератора значение или итератор (генератор) пихает очередное значение основному коду. Простой пример: у нас есть 100 итераторов, представляющих строки в разных файлах (неизвестной длины), и мы хотим вывести все первые строки, потом все вторые, все третьи и т.д. С итераторами а-ля C# или ленивыми списками это делается очень просто. А как это будет выглядеть с генераторами/ФВП?
On 24.11.2010 12:47, Воронков Василий wrote:
> Вот честно, не флейма ради, но недавно подобный вопрос поставил меня в тупик. В > качестве наглядного примера можно взять итераторы в C# или даже более > продвинутую реализацию. Неважно.
Вообще итераторы не нужны. Но придуманы они были для того, чтобы отделить
алгоритмы обработки коллекций от способов доступа к этим коллекциям,
чтобы алгоритмы были бы универсальными.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Вот честно, не флейма ради, но недавно подобный вопрос поставил меня в тупик.
Вот это как-то странно.
ВВ>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.
Ну, во-первых, можно даже сделать более сильное утверждение: итераторы изоморфны событиям.
В твоём примере IEnumerable<int> лёгким движением руки превращается в Action<int>. Это — частный случай того, о чём много и убедительно пишет Барт.
Понятно, что сразу получаем обратный вопрос: а зачем, собственно, нужны события и возможность передавать свою функцию куда-то туда, откуда можно брать и вычитывать значения?
Ответ, конечно же, очевиден: удобным бывает разное представление одного и того же алгоритма. Кроме этого, надо понимать, что дотнет принципиально многоязыковая среда. Потреблять итератор можно и из языка, в котором нет замыканий; потреблять твой вариант — крайне тяжело.
Так что, таки да: "с точки зрения юзабилити это, конечно, менее удобно, но вот, собственно, и все."
Здравствуйте, Sinclair, Вы писали:
S>Ответ, конечно же, очевиден: удобным бывает разное представление одного и того же алгоритма.
Зачем?
S>Кроме этого, надо понимать, что дотнет принципиально многоязыковая среда. Потреблять итератор можно и из языка, в котором нет замыканий; потреблять твой вариант — крайне тяжело.
Язык, в котором нет функций-генераторов, сможет только вызвать такую функцию, и все. Причем для удобной работы с этими функциями все равно должна быть некоторая поддержка со стороны языка, иначе "крайне тяжело" будет работать и с обычными итераторами. Яркий пример — МС++, где даже for each для IEnumerable не было.
Первоклассные же функции имитируются в C# через делегаты, ибо только делегат можно передать в другую функцию. Делегат же — это уже не фича C#. Таким образом, ФВП, написанные на C#, вполне юзабельны и из других .NET-языков — насколько "тяжело" будет с ними работать зависит опять же от того, поддерживает ли язык специальный сахар для делегатов, умеет ли его компилятор создавать замыкания и пр.
Я не вижу здесь какой-то кардинальной разницы.
S>Так что, таки да: "с точки зрения юзабилити это, конечно, менее удобно, но вот, собственно, и все." S>Ну и, конечно же, как всегда, рекомендую почитать Липперта на тему применибельности CPS в C#.
Меня C# вообще мало интересует. Применимо там CPS или неприменимо, насколько оно совместимо с C# программистами и проч. Мне как раз интересно, были ли другие причины, кроме "в .NET есть и другие языки" или "CPS непонятен людям" сделать все так, а не иначе. Липперт как раз на этот вопрос не отвечает.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Меня C# вообще мало интересует. Применимо там CPS или неприменимо, насколько оно совместимо с C# программистами и проч.
Странный вы человек, сначала спрашиваете "нафига итераторы?", а на "в шарпе ими удобно пользоваться" отвечаете, что шарп вас не интересует
ВВ>Мне как раз интересно, были ли другие причины, кроме "в .NET есть и другие языки" или "CPS непонятен людям" сделать все так, а не иначе. Липперт как раз на этот вопрос не отвечает.
Отвечает, и неоднократно. Шарп — мейнстрим; соображения практического удобства ценятся на голову выше, чем абстрактная чистота. Для повышения ЧСВ есть куча других языков и платформ, к чему эти регулярные крестовые походы на <подставить фичу>?
Здравствуйте, Sinix, Вы писали:
ВВ>>Мне как раз интересно, были ли другие причины, кроме "в .NET есть и другие языки" или "CPS непонятен людям" сделать все так, а не иначе. Липперт как раз на этот вопрос не отвечает. S>Отвечает, и неоднократно.
Что отвечает? "Шарп — мейнстрим" это не ответ. Не тот ответ, который мне интересен.
S>Шарп — мейнстрим; соображения практического удобства ценятся на голову выше, чем абстрактная чистота. Для повышения ЧСВ есть куча других языков и платформ, к чему эти регулярные крестовые походы на <подставить фичу>?
Не занимаюсь я никакими крестовыми походами на <подставить фичу>. Говоря, что шарп меня интересует, я имею в виду, что не хочу склонять обсуждение темы к тому, насколько оправдано существование генераторов наряду с замыканиями конкретно в C#. Хотя бы потому что ответы будут "в стиле Липперта". Это не значит, что ответы плохие. Меня просто интересует другое.
Будет проще, если я спрошу, оправдано ли в неком абстрактном языке существование наряду с замыканиями функций-генераторов, какие задачи они могут решать и почему эти задачи не могут быть решены через ФВП с таким же удобством. Неужели обсуждать сферических коней в вакууме проще?
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Что отвечает? "Шарп — мейнстрим" это не ответ. Не тот ответ, который мне интересен.
Так вам уже по-всякому ответили. Делегаты рулят для простого расширения поведения. Итераторы — для работы с последовательностями. То, что одно можно выразить через другое, не значит что остаться должен кто-то один.
Если убирать итераторы — что вы будете делать с foreach? Как вы реализуете linq? Представляете примерный объём работы по переделке рантайма/BCL/документации/инструментария? Нафига?
ВВ>Будет проще, если я спрошу, оправдано ли в неком абстрактном языке существование наряду с замыканиями функций-генераторов, какие задачи они могут решать и почему эти задачи не могут быть решены через ФВП с таким же удобством. Неужели обсуждать сферических коней в вакууме проще?
Да, потому что можно забить на то, насколько таким языком будет удобно пользоваться, какая у него будет популярность и какие шансы на реализацию на нём платформы, аналогичной фреймворку.
Поймите, никого не колышет язык сам по себе, побеждает связка "язык+библиотеки+инструментарий+документация+community". Вы можете сделать сколько угодно сверхфичастых сфероязыков, но пока на них не будет удобно как клепать формочку для студенческой лабораторной, так и писать софт уровня студии — ваш гипотетический язык так и останется игрушкой для гиков.
Здравствуйте, Sinix, Вы писали:
ВВ>>Что отвечает? "Шарп — мейнстрим" это не ответ. Не тот ответ, который мне интересен. S>Так вам уже по-всякому ответили. Делегаты рулят для простого расширения поведения. Итераторы — для работы с последовательностями. То, что одно можно выразить через другое, не значит что остаться должен кто-то один. S>Если убирать итераторы — что вы будете делать с foreach? Как вы реализуете linq? Представляете примерный объём работы по переделке рантайма/BCL/документации/инструментария? Нафига?
Причем тут переделка BCL?
S>Поймите, никого не колышет язык сам по себе, побеждает связка "язык+библиотеки+инструментарий+документация+community". Вы можете сделать сколько угодно сверхфичастых сфероязыков, но пока на них не будет удобно как клепать формочку для студенческой лабораторной, так и писать софт уровня студии — ваш гипотетический язык так и останется игрушкой для гиков.
Форум под названием "Философия программирования" должен быть посвящен по-вашему обсуждению клепания формочек?
Мне одно непонятно — если единственное, что вы можете ответить, это апеллировать к "практичности" и мейнстриму, зачем вообще что-то отвечать? Мне обязательно каждый раз оправдываться за то, что я наступил кому-то на любимую фичу?
Здравствуйте, Воронков Василий, Вы писали:
S>>Если убирать итераторы — что вы будете делать с foreach? Как вы реализуете linq? Представляете примерный объём работы по переделке рантайма/BCL/документации/инструментария? Нафига?
ВВ>Причем тут переделка BCL?
[К.О]
1. Множество методов из BCL принимают/возвращают IEnumerable
2. Придётся протаскивать везде ФВП, да ещё так, чтобы они по производительности не отстали от state-машниы, в которую разворачивается итератор.
[/К.О]
S>>Поймите, никого не колышет язык сам по себе, побеждает связка "язык+библиотеки+инструментарий+документация+community". Вы можете сделать сколько угодно сверхфичастых сфероязыков, но пока на них не будет удобно как клепать формочку для студенческой лабораторной, так и писать софт уровня студии — ваш гипотетический язык так и останется игрушкой для гиков.
ВВ>Форум под названием "Философия программирования" должен быть посвящен по-вашему обсуждению клепания формочек?
Нет, форум под названием "Философия программирования" должен быть как можно дальше оторван от жизни, чтобы красоту мыслей не плющило суровой реальностью.
ВВ>Мне одно непонятно — если единственное, что вы можете ответить, это апеллировать к "практичности" и мейнстриму, зачем вообще что-то отвечать? Мне обязательно каждый раз оправдываться за то, что я наступил кому-то на любимую фичу?
Да можно сделать всё что угодно; в куче языков итераторов в помине нет, и они от этого не сильно страдают. Я всего лишь отметил, что в мейнстрим-языках высокий штиль если и проглядывает, то в первую очередь в упрощённо-приземлённом виде. Потому что людям в первую очередь нужен инструмент для решения насущных проблем; способов изнасиловать мозг и так по горло и выше.
Ты так и не определил, что же такое правильный инструмент. ДАвай я это сделаю за тебя. Из твоего ответа видно, что ты считаешь правильным инструментом тот, который напрямую предназначен для решения этой задачи.
Так ты считаешь?
Так вот реальный мир против. Правильный инструмент это тот который решает поставленную задачу с наименьшими затратами (и сил и времени и всего остального).
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.
Гм... точно ? т.е. быструю сортировку успешно выполнить через замыкания ?
ВВ>Зачем?
Очевидно, для компактности и воспринимаемости записанного кода.
S>>Кроме этого, надо понимать, что дотнет принципиально многоязыковая среда. Потреблять итератор можно и из языка, в котором нет замыканий; потреблять твой вариант — крайне тяжело.
ВВ>Язык, в котором нет функций-генераторов, сможет только вызвать такую функцию, и все. Причем для удобной работы с этими функциями все равно должна быть некоторая поддержка со стороны языка, иначе "крайне тяжело" будет работать и с обычными итераторами. Яркий пример — МС++, где даже for each для IEnumerable не было. ВВ>Первоклассные же функции имитируются в C# через делегаты, ибо только делегат можно передать в другую функцию. Делегат же — это уже не фича C#. Таким образом, ФВП, написанные на C#, вполне юзабельны и из других .NET-языков — насколько "тяжело" будет с ними работать зависит опять же от того, поддерживает ли язык специальный сахар для делегатов, умеет ли его компилятор создавать замыкания и пр.
ВВ>Я не вижу здесь какой-то кардинальной разницы.
Сравним два фрагмента кода на воображаемом языке:
1.
bool hadString = false;
using(StreamWriter sw = OpenWriteStream())
foreach(var s in source.ReadStrings())
{
if (hadString)
sw.Write(", ");
else
hadString = true;
sw.Write(s);
}
2.
using(StreamWriter sw = OpenWriteStream())
{
var sProcessor = new sProcessor(sw);
source.ReadStrings(sProcessor.Process)
}
...
private class sProcessor
{
private bool _hadString = false;
private StreamWriter _sw;
public sProcessor(StreamWriter sw)
{
_sw = sw;
}
public void Process(string s)
{
if (_hadString)
_sw.Write(", ");
else
_hadString = true;
_sw.Write(s);
}
}
В данном случае, разница мне кажется очевидной.
ВВ>Меня C# вообще мало интересует. Применимо там CPS или неприменимо, насколько оно совместимо с C# программистами и проч. Мне как раз интересно, были ли другие причины, кроме "в .NET есть и другие языки" или "CPS непонятен людям" сделать все так, а не иначе. Липперт как раз на этот вопрос не отвечает.
По-моему, вы капризничаете. Фичи не существуют "в воздухе", сами по себе. Они существуют в контексте некоторого фреймворка. В данном конкретном фреймворке есть данности
— существующий интерфейс IEnumerable
— паттерны по его поддержке
— ограничения на то, что можно/нельзя использовать в замыкании
С учётом этих данностей и было принято решение. Как я уже сказал, глобально — никаких причин не делать поддержку "обратных вызовов" нету.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, minorlogic, Вы писали:
ВВ>>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного. M>Гм... точно ? т.е. быструю сортировку успешно выполнить через замыкания ?
Здравствуйте, Sinclair, Вы писали:
ВВ>>Зачем? S>Очевидно, для компактности и воспринимаемости записанного кода.
Это причина, по которой в язык добавляют сахар. Но это не причина для добавления новых фич.
Зайдем с другой стороны. Предположим, что у нас есть воображаемый язык Blab. В языке Blab есть первоклассные функции и замыкания. Язык Blab существует сам по себе, без всяких дотнетов — более того, у него вообще свой собственный бэкенд, спроектированный специально для Blab-а.
Также предположим, что Blab — это функциональный язык.
Blab-еры как-то прознали о том, что существует такая хитрая фича — функции, которые могут возвращать значение несколько раз и к тому же умеют сохранять свое состояние между вызовами. На первый взгляд Блаберам кажется, что фича эта странная и стремная: какие-то побочные эффекты непонятные, вызов и использование таких функций будет сильно отличаться от обычных, с рекурсией внутри них тоже косяки, потом опять же на первый взгляд им кажется, что ничего эта фича в сущности не дает-то. Query Comprehension? Да это же простые комбинаторы. Отложенные вычисления? Так у Блабером для этого санки есть. А на санках и ленивые списки строятся. Через такие функции можно получить универсальный интерфейс для работы с контейнерами? Ну во-первых есть Foldable, а во-вторых — получить-то можно, а как работать с этими контейнерами придется, в императивном стиле? А в Блабе может и вообще циклов не быть.
Однако вот друзья-товарищи из Питона и Руби говорят, что фича мировая. Какие причины эти самые товарищи могут привести, чтобы убедить блаберов, что генераторы их языку, несмотря на вышесказанное, все же не помешают?