Re[13]: Ввод-вывод и чистые функции
От: AlexRK  
Дата: 31.01.17 07:51
Оценка:
Здравствуйте, D. Mon, Вы писали:

ARK>>Верно. Но мы таким образом теряем возможность рассуждать о свойствах программы, глядя в ее код. Видим где-то в середине шаблонного кода вызов map, но чистый он или нет — понять нельзя. Собственно, откуда мы вообще знаем, что map "чистый по построению"? Только посмотрев в исходники (а если их нет?). И если завтра Брайту взбредет в голову сунуть в map чтение файла, мы этого никак не заметим и компилятор нам ничего не скажет.


DM>Скажет. Если ты свою ф-ю описал как pure и вызываешь чужую вроде map, то компилятор убедится, что та чужая тоже чистая.


"Своя" — это та, которая вызывает map, или та, которая передается в map параметром?

DM>Если Брайт вставит в map нечистоты, твоя ф-я, которая ее вызывает, не скомпилируется.


А если "ф-я, которая ее вызывает", не объявлена как pure? То всё, на неожиданные нечистоты реакции компилятора не будет?
Re[16]: Ввод-вывод и чистые функции
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 31.01.17 07:55
Оценка:
Здравствуйте, novitk, Вы писали:

N>От меня ускользает смысл. ТС вроде за аннотацию, ты вроде тоже. 99% в D это обычные грязные функции без аннотации и написать на нем чистую функцию реально нельзя, так как комбинаторы тянут грязь. То есть по факту pure в нем не додумана и не используется.


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

N>>>И два map-a это не решение проблемы, потому что потом нужно два fold-а и вообще всего в std.algorithm или как оно там называется,


Не нужно. Чистота там выводится автоматически.

N>Как мне g переделать в грязную? Я надеюсь какой-то лифтинг все же есть и мне не надо переписывать g еще раз.


int f(int i) pure {
    return i*i;
}
int h(int i) {
    writeln("Bad");
    return i*i;
}

int g(alias func)() { // автоматически чистая или грязная в зависимости от func
    return func(10);
}

int makeSureWeReStillPure() pure {
    return g!f + 1;
}

void main(string[ ] args)
{
    writeln(g!f); 
    writeln(g!h); 
    writeln(makeSureWeReStillPure());
}

100
Bad
100
101
Отредактировано 31.01.2017 7:59 D. Mon . Предыдущая версия .
Re[14]: Ввод-вывод и чистые функции
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 31.01.17 08:01
Оценка:
Здравствуйте, AlexRK, Вы писали:

DM>>Скажет. Если ты свою ф-ю описал как pure и вызываешь чужую вроде map, то компилятор убедится, что та чужая тоже чистая.


ARK>"Своя" — это та, которая вызывает map, или та, которая передается в map параметром?


Которая вызывает.

DM>>Если Брайт вставит в map нечистоты, твоя ф-я, которая ее вызывает, не скомпилируется.


ARK>А если "ф-я, которая ее вызывает", не объявлена как pure? То всё, на неожиданные нечистоты реакции компилятора не будет?


Если вызывающая не объявлена pure, значит ты от кода чистоты не требуешь явно, значит разрешаешь грязный код. Какая реакция компилятора тут нужна?
Re[15]: Ввод-вывод и чистые функции
От: AlexRK  
Дата: 31.01.17 08:09
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Если вызывающая не объявлена pure, значит ты от кода чистоты не требуешь явно, значит разрешаешь грязный код. Какая реакция компилятора тут нужна?


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

Насчет реакции компилятора согласен, в данном случае наверное никакой быть не может.
Re[16]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 08:35
Оценка:
Здравствуйте, novitk, Вы писали:

_>>Не, я согласен что явная аннотация важна.

_>>1. Это никак формально не гарантируется компилятором (а вот в D это добавили)
_>>2. Факт данной чистоты никак потом на практике не используется (в том числе и в D, причём скорее вследствие отсутствия соответствующих навыков у прикладных программистов).
N>От меня ускользает смысл. ТС вроде за аннотацию, ты вроде тоже. 99% в D это обычные грязные функции без аннотации и написать на нем чистую функцию реально нельзя, так как комбинаторы тянут грязь. То есть по факту pure в нем не додумана и не используется.

Ситуация на самом деле очень простая: язык предоставляет все условия для построение внутри приложения определённой части с гарантированной (компилятором) чистотой, но большинство программистов (т.к. это в основном перешедшие из мира C++ люди) игнорируют данную возможность в своей работе. Соответственно это направление не особо развивается.

Что касается "99% в D это обычные грязные функции без аннотации", то я уже писал выше, что это не верно. В D общепринятой практикой является развитое МП, так что большинство функций — это "не просто функции" и их аргументы — это не просто обычные типизированные параметры.

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

N>>>И два map-a это не решение проблемы, потому что потом нужно два fold-а и вообще всего в std.algorithm или как оно там называется,

_>>Ну как бы вообще говоря решение. Только совсем не изящное.
N>
N>int f(int i) pure {
N>    return i*i;
N>}
N>int h(int i) {
N>    writeln("Bad");
N>    return i*i;
N>}
N>int g(int function(int) pure func) pure {
N>    return func(10);
N>}
N>void main(string[ ] args)
N>{
N>   writeln(g(&f)); //no prob
N>   writeln(g(&h)); //???
N>}
N>

N>main.d(18): Error: function main.g (int function(int) pure func) is not callable using argument types (int function(int i))
N>Как мне g переделать в грязную? Я надеюсь какой-то лифтинг все же есть и мне не надо переписывать g еще раз.

Надо изначально записывать g с обобщённым аргументом, а не с жёстким требованием на чистоту и никаких проблем не будет.

_>>В D заложена потенциальная возможность этого. Но на практике (в библиотеках, в прикладном коде) она почти не используется.

N>Если функционал не используется, то в нем нет смысла. Пока есть сильное подозрение что авторы этой фичи в D просто не додумкали и/или не разобрались с матаном.

Нет, в язык всё введено нормально. Разве что стоит добавить одну мелочь (про которую я писал выше, с автоматической расстановкой атрибута), но и так пока можно пользоваться. Проблема именно в практике использования данной возможности. Создание библиотек заточенных на это и т.п.
Re[16]: Ввод-вывод и чистые функции
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 31.01.17 08:35
Оценка: 3 (1) +1
Здравствуйте, AlexRK, Вы писали:

DM>>Если вызывающая не объявлена pure, значит ты от кода чистоты не требуешь явно, значит разрешаешь грязный код. Какая реакция компилятора тут нужна?


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


Можно локально посреди грязной ф-ии для кусочка кода потребовать чистоты. Пишешь
() pure {
  x = xs.map!f.filter!g.reduce!h;  // можно, пока все чистое
  y = x + f(42); 
  // writeln("io"); // а вот этого компилятор не даст!
} ();

Корявенько, но можно.

ARK>Но, поскольку map явно не объявлен как pure, то я должен ожидать от него чего угодно.


Ну да, это ж не хаскель, раз у нас дефолт это грязные ф-ии, то любое отличие от дефолта надо требовать явно. Можно в одном месте программы проверить, что вызов map с чистым аргументом все еще чист. Тогда если реализация map станет грязной, программа не скомпилится.
Re[5]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 08:39
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Тут какой-то ерунды наговорили. В D map это не функция, это шаблон функции, генерик. Для шаблонов всегда работает автоматический вывод атрибутов, в том числе чистоты. Если в map передать чистую функцию, получится чистая функция. Если передать грязную — получится грязная.


Тут обсуждается немного другое. Не просто возможность "передать" чистоту дальше, а возможность изменения поведения функции (допустим применения каких-то более эффективных алгоритмов) в случае передачи чистого параметра и т.п. Я уже отвечал тут людям, что все возможности для этого есть (т.к. доступ к атрибутам передаваемого типа имеется). Но вот никаких практических реализации данной возможности я не припомню.
Re[17]: Ввод-вывод и чистые функции
От: AlexRK  
Дата: 31.01.17 08:41
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Можно локально посреди грязной ф-ии для кусочка кода потребовать чистоты. Пишешь


Ха-ха, забавно. Вообще D прикольный язычок, я смотрю.
Re[13]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 08:50
Оценка:
Здравствуйте, D. Mon, Вы писали:

ARK>>Верно. Но мы таким образом теряем возможность рассуждать о свойствах программы, глядя в ее код. Видим где-то в середине шаблонного кода вызов map, но чистый он или нет — понять нельзя. Собственно, откуда мы вообще знаем, что map "чистый по построению"? Только посмотрев в исходники (а если их нет?). И если завтра Брайту взбредет в голову сунуть в map чтение файла, мы этого никак не заметим и компилятор нам ничего не скажет.

DM>Скажет. Если ты свою ф-ю описал как pure и вызываешь чужую вроде map, то компилятор убедится, что та чужая тоже чистая. Если Брайт вставит в map нечистоты, твоя ф-я, которая ее вызывает, не скомпилируется. map и другие ФВП в D не грязные, они с автоматическим выводом чистоты.

К сожалению последнее работает не для всего. Простейший пример:

auto f(int x) {return x*2;}

writeln(__traits(getFunctionAttributes, f)); //pure нет
writeln(__traits(getFunctionAttributes, function (int x)=>x*2)); //pure есть


Кстати, как раз поэтому твой пример (https://dpaste.dzfl.pl/42460904b633) работает. Если заменить там "x => x*2" на подобную f, то уже не скомпилируется. Хотя по смыслу ничего не поменяется. Вот эту недоработку в работе компилятора я бы всё же поправил.
Re[8]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 08:57
Оценка:
Здравствуйте, D. Mon, Вы писали:

_>>Другое дело (и это справедливо отмечено в заданном на SO вопросе), что реализации алгоритмов из стандартной библиотеки в данный момент ни сами не отмечены как pure, ни требуют этого от передаваемых в них функций. Правильно это или нет — это другой вопрос, зависящий на мой взгляд от того, какого больше стиля придерживается усреднённый программист на D. Но в любом случае у желающего есть все возможности сделать любой вариант. Ну и да, наверное в идеале в будущем иметь в стандартной библиотеке все возможные реализации. )

DM>Там уже есть все реализации автоматически. В стандартной библиотеке все на шаблонах, передаешь чистую функцию — получаешь чистую. Компилятор по-прежнему все контролирует, а код засирать не обязательно.

Если мы хотим какую-то пользу от всего этого, то как раз "засирать код" (плодить новые версии стандартных функций) надо. Например иметь вариант функций с мемоизацией и т.п.
Re[16]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 09:09
Оценка:
Здравствуйте, AlexRK, Вы писали:

DM>>Если вызывающая не объявлена pure, значит ты от кода чистоты не требуешь явно, значит разрешаешь грязный код. Какая реакция компилятора тут нужна?

ARK>Ну, я не требую от всего кода функции чистоты, но хочу быть при этом уверенным, что map не будет писать в файлы, даже несмотря на то, что вызывается из глязной функции.
ARK>Но, поскольку map явно не объявлен как pure, то я должен ожидать от него чего угодно.

На уровне документации кода — да.

ARK>Насчет реакции компилятора согласен, в данном случае наверное никакой быть не может.


А вот на уровне компилятора это решается просто:
static assert ([__traits(getFunctionAttributes, my_func)[]].canFind("pure"), "Impure!");
Re[14]: Ввод-вывод и чистые функции
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 31.01.17 10:38
Оценка:
Здравствуйте, alex_public, Вы писали:

DM>>Скажет. Если ты свою ф-ю описал как pure и вызываешь чужую вроде map, то компилятор убедится, что та чужая тоже чистая. Если Брайт вставит в map нечистоты, твоя ф-я, которая ее вызывает, не скомпилируется. map и другие ФВП в D не грязные, они с автоматическим выводом чистоты.


_>К сожалению последнее работает не для всего.


Автоматический вывод работает только на шаблонных ф-ях. Но в стандартной библиотеке (и не только) ФВП как раз такие. И свои ФВП стоит в том же стиле писать, тогда все будет выводиться, а заодно и инлайниться.
Re[5]: Ввод-вывод и чистые функции
От: Nick Linker Россия lj://_lcr_
Дата: 31.01.17 10:54
Оценка:
alex_public,

_>Никто не запрещает, в том то и вся прелесть (в сравнение с Хаскелем). В тоже время у тебя в языке есть все механизмы для введения подобного запрета при желание (делается в одну короткую строчку). Т.е. язык даёт тебя полную свободу и плюс все необходимые инструменты для построения любого варианта.


То есть ты считаешь, что запреты — это плохо и это ограничивают наш полёт мысли, правильно? И чем меньше ограничений, тем выше полёт мысли?

_>Это всё следствия более общего вопроса в построение языка: мультипарадигменный он или нет. Современные удобные языки легко позволяют писать и в ООП стиле и ФП стиле и даже закопаться в МП. А можно смешать это всё в одном проекте и всё равно всё будет удобно. Именно поэтому у языка D (сильно мультипарадигменного) есть хоть какие-то шансы на большой успех (там уже больше от "политики" зависит), а у того же однопарадигменного Хаскеля таких шансов не было от рождения.


Проблема с походом Haskell в массы не в том, что он "однопарадигменный", а в том что он сложный — сложность Хаскеля не в монадах (это просто непривычная для императивщиков концепция), а в сильно продвинутых типах, которых нигде кроме экспериментальных языков нет (пример: Type-level insertion sort).
Потому в продакшене Хаскель звёзд с неба не хватает, но кое-какие применения имеет: https://github.com/commercialhaskell/commercialhaskell/blob/master/README.md

А мультипарадигменность из коробки с одной стороны позволяет программисту Васе в D писать как на фортране или Пете — как на джаве, а с другой стороны выходит боком в долгосрочной преспективе — примеров тому предостаточно.
Когда в языке из коробки нет, например, наследования и обработки исключений — это сразу делает язык гораздо проще для реализации и для изучения. И соотвественно шансы стать массовым увеличиваются.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[15]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 11:06
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>>>Скажет. Если ты свою ф-ю описал как pure и вызываешь чужую вроде map, то компилятор убедится, что та чужая тоже чистая. Если Брайт вставит в map нечистоты, твоя ф-я, которая ее вызывает, не скомпилируется. map и другие ФВП в D не грязные, они с автоматическим выводом чистоты.

_>>К сожалению последнее работает не для всего.
DM>Автоматический вывод работает только на шаблонных ф-ях. Но в стандартной библиотеке (и не только) ФВП как раз такие. И свои ФВП стоит в том же стиле писать, тогда все будет выводиться, а заодно и инлайниться.

Я не много не про это. Я про то, что для лямбд атрибут чистоты автоматически проставляется компилятором (и поэтому твой пример с "map!(x => x*2)" работал), а для обычных функций это почему-то не реализовано. Так что даже если я написал реально чистую функцию f, но забыл (или как большинство императивных программистов просто не в курсе про данный атрибут) проставить ей атрибут pure, то твой пример с map!(f) уже не будет работать.
Re[16]: Ввод-вывод и чистые функции
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 31.01.17 11:31
Оценка:
Здравствуйте, alex_public, Вы писали:

DM>>Автоматический вывод работает только на шаблонных ф-ях. Но в стандартной библиотеке (и не только) ФВП как раз такие. И свои ФВП стоит в том же стиле писать, тогда все будет выводиться, а заодно и инлайниться.


_>Я не много не про это. Я про то, что для лямбд атрибут чистоты автоматически проставляется компилятором (и поэтому твой пример с "map!(x => x*2)" работал), а для обычных функций это почему-то не реализовано. Так что даже если я написал реально чистую функцию f, но забыл (или как большинство императивных программистов просто не в курсе про данный атрибут) проставить ей атрибут pure, то твой пример с map!(f) уже не будет работать.


Как раз про это. Потому что упомянутая лямбда — это шаблонная ф-я тоже, вот для нее и выводится. Замени ее нешаблонной ф-ей, перестанет выводиться.
Re[6]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 13:12
Оценка:
Здравствуйте, Nick Linker, Вы писали:

_>>Никто не запрещает, в том то и вся прелесть (в сравнение с Хаскелем). В тоже время у тебя в языке есть все механизмы для введения подобного запрета при желание (делается в одну короткую строчку). Т.е. язык даёт тебя полную свободу и плюс все необходимые инструменты для построения любого варианта.

NL>То есть ты считаешь, что запреты — это плохо и это ограничивают наш полёт мысли, правильно? И чем меньше ограничений, тем выше полёт мысли?

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

_>>Это всё следствия более общего вопроса в построение языка: мультипарадигменный он или нет. Современные удобные языки легко позволяют писать и в ООП стиле и ФП стиле и даже закопаться в МП. А можно смешать это всё в одном проекте и всё равно всё будет удобно. Именно поэтому у языка D (сильно мультипарадигменного) есть хоть какие-то шансы на большой успех (там уже больше от "политики" зависит), а у того же однопарадигменного Хаскеля таких шансов не было от рождения.

NL>Проблема с походом Haskell в массы не в том, что он "однопарадигменный", а в том что он сложный — сложность Хаскеля не в монадах (это просто непривычная для императивщиков концепция), а в сильно продвинутых типах, которых нигде кроме экспериментальных языков нет (пример: Type-level insertion sort).
NL>Потому в продакшене Хаскель звёзд с неба не хватает, но кое-какие применения имеет: https://github.com/commercialhaskell/commercialhaskell/blob/master/README.md

Да ничего в нём нет сложного. Просто неудобен он почти для всех применений. За исключением разве что написания каких-нибудь консольных утилит анализа/трансформации файлов и т.п. приложений.

NL>А мультипарадигменность из коробки с одной стороны позволяет программисту Васе в D писать как на фортране или Пете — как на джаве, а с другой стороны выходит боком в долгосрочной преспективе — примеров тому предостаточно.


В долгосрочной перспективе всё очень хорошо видно здесь: http://www.tiobe.com/tiobe-index/ )))

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


Это речь про Go? )))
Re[17]: Ввод-вывод и чистые функции
От: alex_public  
Дата: 31.01.17 13:15
Оценка:
Здравствуйте, D. Mon, Вы писали:

_>>Я не много не про это. Я про то, что для лямбд атрибут чистоты автоматически проставляется компилятором (и поэтому твой пример с "map!(x => x*2)" работал), а для обычных функций это почему-то не реализовано. Так что даже если я написал реально чистую функцию f, но забыл (или как большинство императивных программистов просто не в курсе про данный атрибут) проставить ей атрибут pure, то твой пример с map!(f) уже не будет работать.

DM>Как раз про это. Потому что упомянутая лямбда — это шаблонная ф-я тоже, вот для нее и выводится. Замени ее нешаблонной ф-ей, перестанет выводиться.

А где ты видишь тут шаблонную функцию:
writeln(__traits(getFunctionAttributes, function (int x)=>x*2)); //pure есть


Ну и в любом случае, не в этом суть, а в том что для обычных функций оно не работает. А надо чтобы работало для всех (чей исходный код доступен).
Re[17]: Ввод-вывод и чистые функции
От: novitk США  
Дата: 31.01.17 14:05
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Это ваши выводы не додуманы. Никакую грязь комбинаторы не тянут, их отлично себе можно использовать в чистом коде.

Я не знаю D. Было сообщения что комбинаторы в std.algorithm не имеют pure, что я понял как грязь. Если они сделаны через MP, то это меняет дело.

N>>Как мне g переделать в грязную? Я надеюсь какой-то лифтинг все же есть и мне не надо переписывать g еще раз.

DM>[ccode]
DM>int g(alias func)() { // автоматически чистая или грязная в зависимости от func
DM> return func(10);
DM>}
DM>[/code]
А типизацию в параметр можно вернуть? Иначе как я вообще пойму в сложной HOF, какая сигнатура должна быть у func?
Re[18]: Ввод-вывод и чистые функции
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 31.01.17 15:22
Оценка:
Здравствуйте, novitk, Вы писали:

N>А типизацию в параметр можно вернуть? Иначе как я вообще пойму в сложной HOF, какая сигнатура должна быть у func?


Да, можно вернуть, только с другим синтаксисом — через статическую интроспекцию.
int g(alias func)() 
if (isCallable!func && is(ReturnType!func==int) && Parameters!func.length==1 && is(Parameters!func[0]==int)) { 
    return func(10);
}

Сюда же можно кое-какие проверки на поведение func вставить, а также кастомные сообщения об ошибках.
Выходит громоздко, может быть можно как-то проще.
Re[19]: Ввод-вывод и чистые функции
От: novitk США  
Дата: 31.01.17 16:09
Оценка:
Здравствуйте, D. Mon, Вы писали:

DM>Сюда же можно кое-какие проверки на поведение func вставить, а также кастомные сообщения об ошибках.

IMXO Микроскопом тут забивают гвоздь, выглядит приемлемо только для людей ничего не видевших кроме МП в C++.

DM>Выходит громоздко, может быть можно как-то проще.

Возник вопрос на который не могу сходу ответить. Почему бы не сделать неявный каст в грязь, если хотя бы один из параметров чистой HOF грязный, тo есть просто разрешить выражение "pureHOF(&dirtyFunc)" в грязной функции? Это в принципе соответствует моему пожеланию о автолифтинге. Замыкания/лямбды полученные таким образом, через карринг или еще как, тоже конечно грязные. Какие тут проблемы?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.