Побочные эффекты и запись чтение в базу данных
От: salog  
Дата: 29.04.08 00:33
Оценка:
Функциональное программирование — это прежде всего отсутствие побочных эффектов, то есть когда "суперпозиция" функций дает один и тот же результат независимо от последовательности выполнения. Гарантией отсутствия побочного эффекта является невозможность функцией повлиять на внешние данные. Функция работает только со своими аргументами. Так пишется в книжках по функциональному программированию.

Тогда как быть с записью и чтение в/из базы данных? Данные базы данных — это и есть внешние данные. И если одан функция помимо того что делает что то с аргументами, но еще и читает кое какие данные из базы данных, а другая, помимо того что тоже что то делает с аргументами — пишет что то в базу данных, то это и будет давать те самые побочные эффекты.
Конечно смысла в этом никакого, результат непредсказуем, но, подключая модули к языкам типа Python или OCAML можно однако отклониться от генерального курса и опять попасть в пучину императивного стиля.
Я к тому что — тут опять человеческий фактор влазит.
Re: Побочные эффекты и запись чтение в базу данных
От: Аноним  
Дата: 29.04.08 03:23
Оценка:
Открыл америку... Читай про монады.
Re: Побочные эффекты и запись чтение в базу данных
От: Кодёнок  
Дата: 29.04.08 04:38
Оценка: 1 (1)
Здравствуйте, salog, Вы писали:

S>Тогда как быть с записью и чтение в/из базы данных?


Состояние мира (база данных — часть его) представляется в виде бесконечного списка разных состояний
[world1, world2, world3, ...]

каждая функция, производящая эффекты, берет текущее состояние и возвращает следующее
Модификация базы данных — это приведение мира в другое состояние.
function :: (worldOld, params) -> (worldNew, result)
Чтение из базы, в которую за это время кто-то мог написать — это узнавание состояния мира.
readValue :: (worldState, value)

В сложных функциях это более чем неудобно и делает их нечитаемыми.

Монада IO позволяет скрыть ручную передачу состояний, переделав функцию в
function :: IO params -> IO result

Что с помощью стандартных функций для работы с монадами типа
liftM :: Monad a => (b -> c) -> a b -> a c.

позволяет записать её в привычном виде
function2 :: params -> result

для данного конкретного случая liftM работает так
liftM :: (params -> result) -> IO params -> IO result
т.е. берет функцию и оборачивает её параметры и результаты в монаду IO, возвращая новую функцию.

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

Состояния считаются неконтроллируемыми и неоптимизируемыми, т.е. компилятор не имеет права ни одной оптимизации этой передачи состояний сделать, как бы они не были представлены (я тут заводил ветку на этот счет). Это само собой хак, реализованный в компиляторе. Из чего собственно следует, что вот эти три вещи принципиально не могут существовать одновременно:
— программирование свободное от побочных эффектов
— моделирование побочных эффектов состоянием мира
— мега-оптимизатор, который теоретически возможен в чистой программе, и на изобретение которого надеются функциональщики

Как говорится, выберите любые две.
Re[2]: Побочные эффекты и запись чтение в базу данных
От: Курилка Россия http://kirya.narod.ru/
Дата: 29.04.08 05:17
Оценка:
Здравствуйте, http://migmit.vox.com/, Вы писали:

HMV>Открыл америку... Читай про монады.


Ну не монадами едиными вроде как
Re[2]: Побочные эффекты и запись чтение в базу данных
От: salog  
Дата: 29.04.08 07:32
Оценка:
Здравствуйте, Кодёнок, Вы писали:

Кё>Здравствуйте, salog, Вы писали:


S>>Тогда как быть с записью и чтение в/из базы данных?


Кё>Состояния считаются неконтроллируемыми и неоптимизируемыми, т.е. компилятор не имеет права ни одной оптимизации этой передачи состояний сделать, как бы они не были представлены (я тут заводил ветку на этот счет). Это само собой хак, реализованный в компиляторе. Из чего собственно следует, что вот эти три вещи принципиально не могут существовать одновременно:

Кё>- программирование свободное от побочных эффектов
Кё>- моделирование побочных эффектов состоянием мира
Кё>- мега-оптимизатор, который теоретически возможен в чистой программе, и на изобретение которого надеются функциональщики

Кё>Как говорится, выберите любые две.


Функциональный компилятор считает, что функции побочных эффектов не имеют, поэтому конечная последовательность вычислений (которая может быть и параллельной) зависит только взаимной зависимости функций. То есть функция a1, зависящая от функции b1 не может быть вычислена раньше чем b1. Вроде как... (у меня все еще императивное мышление).
Хуже когда две функции напрямую не зависят друг от друга и для функционального компилятора они независимы и могут быть выполнены в любом порядке, но при этом они косвенно связаны через таблицу в базе данных.
Т.е. например a1 читает данные из таблицы t1, в которую записывает функция b1.
И в зависимости от цели: хотим ли мы, чтобы a1 прочла данные ПОСЛЕ записи или ДО, компилятору нужно явно указать в какой зависимости находятся a1 и b1. Зависит ли a1 от b1 или наоборот. Но, поскольку явной, первичной зависимости нет, но указанная зависимость это что то вроде "директивы препроцессору", дополнительное знание сообщаемое компилятору извне (программистом) о тех вещах о которых он знать не может поскольку "не видит всей картины".
Я правда не знаю при чем в этой цепочке рассуждений МОНАДЫ?
Re[3]: Побочные эффекты и запись чтение в базу данных
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 29.04.08 08:25
Оценка:
Здравствуйте, salog, Вы писали:

S>Я правда не знаю при чем в этой цепочке рассуждений МОНАДЫ?


Ты внимательно прочитал, что написал Кодёнок? Есть состояние мира, и каждое последующее его значение зависит от предыдущего. Следовательно, компилятор _знает_, какую функцию надо выполнить первой, какую следующей.

Рекомендую очень хорошую статью на эту тему IO inside.
Где то был русский перевод.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Побочные эффекты и запись чтение в базу данных
От: Аноним  
Дата: 29.04.08 15:23
Оценка:
Здравствуйте, Кодёнок, Вы писали:

Кё>..Это само собой хак, реализованный в компиляторе. ...


Ну не такой уж и хак, по сравнению с другими подходами. В статье Вадлера imperative functional programming сказано, что можно обойтись введением буквально одного примитива в компилятор. И вся фишка этого примитива только в том, что он непрозрачен для оптимизатора. ИМХО, это и хаком то не назвать, вполне красивое решение.
Re: Побочные эффекты и запись чтение в базу данных
От: BulatZiganshin  
Дата: 30.04.08 07:30
Оценка:
Здравствуйте, salog, Вы писали:

S>Тогда как быть с записью и чтение в/из базы данных?


http://www.haskell.org/haskellwiki/Ru/IO
Люди, я люблю вас! Будьте бдительны!!!
Re: Побочные эффекты и запись чтение в базу данных
От: VladD2 Российская Империя www.nemerle.org
Дата: 30.04.08 18:29
Оценка: +1 :)
Здравствуйте, salog, Вы писали:

S>Тогда как быть с записью и чтение в/из базы данных?


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

Везде есть два подхода 1) прагматичный и 2) фанатичный. На мой взгляд прагматичный подход в данном вопросе признаться себе, что весь мир в доску императивен, и только отдельные его части можно представить как чистую математику. Вот эти части и надо рассматривать как функциональные вычисления, а к БД лучше подходить с императивным мировозрением.

В общем, надо для себя решить является ли функциональное программирование самоцелью или это всего лишь способ облегчить решение задачи. Если второе, то ФП надо рассматривать как средство повышения декларативности в программах. Тогда SQL можно рассматривать как очень высокоуровневый, декларативный язык специально созданный для обработки данных в БД (и превосходящий любые универсальные языки), а стало быть более предпочтительный. Общение с внешним миром рассматривать как банальную императивную передачу данных. Ну, а если первое, то лучше вообще забить на программирование и искать холивар по круче.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Побочные эффекты и запись чтение в базу данных
От: salog  
Дата: 03.05.08 09:35
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, salog, Вы писали:


S>>Тогда как быть с записью и чтение в/из базы данных?


VD>Очень просто! Начинаем эмулировать императивность параллельно трахая себе и окружающим мозг разными штучкодрючками вроде монад и миров.


VD>Везде есть два подхода 1) прагматичный и 2) фанатичный. На мой взгляд прагматичный подход в данном вопросе признаться себе, что весь мир в доску императивен, и только отдельные его части можно представить как чистую математику. Вот эти части и надо рассматривать как функциональные вычисления, а к БД лучше подходить с императивным мировозрением.


VD>В общем, надо для себя решить является ли функциональное программирование самоцелью или это всего лишь способ облегчить решение задачи. Если второе, то ФП надо рассматривать как средство повышения декларативности в программах. Тогда SQL можно рассматривать как очень высокоуровневый, декларативный язык специально созданный для обработки данных в БД (и превосходящий любые универсальные языки), а стало быть более предпочтительный. Общение с внешним миром рассматривать как банальную императивную передачу данных. Ну, а если первое, то лучше вообще забить на программирование и искать холивар по круче.



Да, что то у меня тоже мозг задымился от монад а особенно от миров. Вообщем то, то что функция ввода или вывода чтот о там изменила вовне — совсем не страшно. Хуже когда другая часть программы из этого же источника читает. И тогда возникает проблема последовательности выполнения. Что за чем. Сначала пишем а потом читае или наоборот. А это уже определяет программист.

В таком случае, почему бы не ввести оператор взаимной зависимости =- это когда вычисление функции основывается на результатах выполнения других функций как и положено в функциональных вычислениях, но разрешение на выполенение определеяется операторами зависимости. Все сразу бы упростилось.
Re[3]: Побочные эффекты и запись чтение в базу данных
От: Курилка Россия http://kirya.narod.ru/
Дата: 03.05.08 09:42
Оценка:
Здравствуйте, salog, Вы писали:

S>В таком случае, почему бы не ввести оператор взаимной зависимости =- это когда вычисление функции основывается на результатах выполнения других функций как и положено в функциональных вычислениях, но разрешение на выполенение определеяется операторами зависимости. Все сразу бы упростилось.


Т.е. помимо иерархии вызовов функций ты хочешь ввести параллельно иерархию "зависимостей"?
Кстати, а что такое "разрешение на выполнение"? Особенно в условиях зависимостей по файлам и сети?
Схема твоя, скорее всего, будет работать вполне нормально для статических систем, но вот если добавить туда "честный" IO (внешний по отношению к твоей программе), то всё становится снова весело и снова вылезут или монады или чистая императивщина (ну или иной способ описания взаимодействия с внешним миром).
Всё это, безусловно, лишь сугубо моё имхо, не более
Re: Побочные эффекты и запись чтение в базу данных
От: Gaperton http://gaperton.livejournal.com
Дата: 03.05.08 13:49
Оценка: +2
Здравствуйте, salog, Вы писали:

S>Тогда как быть с записью и чтение в/из базы данных? Данные базы данных — это и есть внешние данные. И если одан функция помимо того что делает что то с аргументами, но еще и читает кое какие данные из базы данных, а другая, помимо того что тоже что то делает с аргументами — пишет что то в базу данных, то это и будет давать те самые побочные эффекты.


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

S>Я к тому что — тут опять человеческий фактор влазит.


Человеческий фактор вообще всюду влазит. В том числе и тогда, когда ты пишешь на С++ — постоянно "рискуешь попасть в пучину" С-шного процедурного стиля.
Re[4]: Побочные эффекты и запись чтение в базу данных
От: salog  
Дата: 04.05.08 00:34
Оценка:
Здравствуйте, Курилка, Вы писали:

К>Здравствуйте, salog, Вы писали:


S>>В таком случае, почему бы не ввести оператор взаимной зависимости =- это когда вычисление функции основывается на результатах выполнения других функций как и положено в функциональных вычислениях, но разрешение на выполенение определеяется операторами зависимости. Все сразу бы упростилось.


К>Т.е. помимо иерархии вызовов функций ты хочешь ввести параллельно иерархию "зависимостей"?

К>Кстати, а что такое "разрешение на выполнение"? Особенно в условиях зависимостей по файлам и сети?

Под словами "разрешение на выполнение" и "оператор зависимости" я имел ввиду по смыслу одно и то же. Вообщем то, наверное можно обойтись и стандартным механизмом — иерархии функций. То есть, для определения зависимости, функции А от функции В, которая однако функции А никаких дянных не передает (или, например, передает всегда 1 как фиктивный возврат), достаточно предусмотреть аргумент в функции А, который принимает функцию В.
Но тут есть одно "НО", связанное с типизацией. Получается что "зависимость" становится неотъемлемой части функции, в то время как следовало бы при проектировании включать в функции только параметры относящиеся к её полезной работе. А метаданные управления выполнением все же выносить за пределы функций.
Хотя возожен и другой вариант: управление (в том числе через фиктивные параметры, управляющие зависимостью, а следовательно и последовательностью выполнения) полагается на функции-обертки, включающие в себя тем или иным образом единожды спректированные "функции ядра".

К>Схема твоя, скорее всего, будет работать вполне нормально для статических систем, но вот если добавить туда "честный" IO (внешний по отношению к твоей программе), то всё становится снова весело и снова вылезут или монады или чистая императивщина (ну или иной способ описания взаимодействия с внешним миром).


А чем отличается IO от вычисления? Тем что вычисление вычисляет на основе входящих параметров, а IO берет данные из "космоса"?
Хорошо, если чтобы вычислить логарифм я не начинаю расчеты, а обращаюсь к таблице значений (помните в школе так делали), то чем это хуже? Это может быть даже быстрее.
Следущий шажок: если вычисление должно зависеть от времени суток или фазы луны? Или иметь элемент случайного числа? И кроме того, если мы ставим себе цель произвести вычисления с элементом случайности и непредсказуемости?
А IO — это частный случай. И собственно никто и не стремится к стабильности (неизменности вычилений). Это никому не нужно. Важно — сохранение логической целостности и выполнение определенных целей, которые задаются как отношение частей. Один из элементов этих отношений — зависимость. А как её можно было бы реализовать средствами ФП, на мой взгляд, я написал выше.
Re[5]: Побочные эффекты и запись чтение в базу данных
От: Курилка Россия http://kirya.narod.ru/
Дата: 04.05.08 04:06
Оценка:
Здравствуйте, salog, Вы писали:

S>А чем отличается IO от вычисления? Тем что вычисление вычисляет на основе входящих параметров, а IO берет данные из "космоса"?

S>Хорошо, если чтобы вычислить логарифм я не начинаю расчеты, а обращаюсь к таблице значений (помните в школе так делали), то чем это хуже? Это может быть даже быстрее.
S>Следущий шажок: если вычисление должно зависеть от времени суток или фазы луны? Или иметь элемент случайного числа? И кроме того, если мы ставим себе цель произвести вычисления с элементом случайности и непредсказуемости?
S>А IO — это частный случай. И собственно никто и не стремится к стабильности (неизменности вычилений). Это никому не нужно. Важно — сохранение логической целостности и выполнение определенных целей, которые задаются как отношение частей. Один из элементов этих отношений — зависимость. А как её можно было бы реализовать средствами ФП, на мой взгляд, я написал выше.

Как раз наоборот, всё вышеописанное — частные случаи монады IO
А чёткого описания реализации предлагаемого решения лично я так и не увидел. Реально вижу, что ты пытаешься статически описать взаимосвязи функций, включая неявные, тем самым фактически выкидывая динамическую составляющую, которая есть практически в любой вменяемой системе (e.g. действия пользователя).
Re[3]: Побочные эффекты и запись чтение в базу данных
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 04.05.08 13:08
Оценка: +1
Здравствуйте, salog, Вы писали:

S>В таком случае, почему бы не ввести оператор взаимной зависимости =- это когда вычисление функции основывается на результатах выполнения других функций как и положено в функциональных вычислениях, но разрешение на выполенение определеяется операторами зависимости. Все сразу бы упростилось.


Чем он отличается от оператора bind (>>=) в монадах?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Побочные эффекты и запись чтение в базу данных
От: BulatZiganshin  
Дата: 04.05.08 17:27
Оценка:
Здравствуйте, salog, Вы писали:

поздравляю, ты изобрёл монаду IO

читай http://haskell.org/haskellwiki/IO_inside
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: Побочные эффекты и запись чтение в базу данных
От: Mirrorer  
Дата: 04.05.08 17:37
Оценка:
Здравствуйте, VladD2, Вы писали:

S>>Тогда как быть с записью и чтение в/из базы данных?


VD>Очень просто! Начинаем эмулировать императивность параллельно трахая себе и окружающим мозг разными штучкодрючками вроде монад и миров.


Ыххх. Ну что ж, поехали в n+1 раз...

Есть в Java такая штука как Checked Exceptions. кому-то она нравится, кому-то нет но суть ее заключается в следующем.
public void SomeMethod throws SomeException
{
   if(somtehingWrong)
     throw new SomeException();
}
]

В чем плюс данного подхода. В том что не заглядівая внутрь метода, можно сказать, что внутри него происходит какая-то ботва, в результате которой может произойти SomeException. Более того. Если написать второй метод использующий первый
public void AnotherMethod
{
     SomeMethod();
}

то компилятор скажет, что надо бы добавить
throws SomeException


Таким образом, как бы глубоко не происходила какая-то ботва, по сигнатуре самого верхнего метода мы можем сказать, что да, где-то внутри может быть чего-то не так. И избавится от этого throws SomeException нам не удастся. Ибо.

Ехаем далее. Допустим мы хотим реализовать цепочку вызовов методов, каждый из который может завершится с ошибкой.
Ниже решение влоб на псевдокоде
bool func1(double randomRange)
{
   if(Random.next() >= randomRange)   return true;
   else    return false;
}

bool combination()
{
   if(func1(0.5)) 
   {
       if(func1(0.3))
       {
           if(func1(0.7))
           {
                Print("Gotcha!!");
                return true;
           }else return false;
       }else return false;
   }else return false;
}

Выглядит не очень симпатично. Гораздо удобнее если бы был хитрый метод combine, который бы позволял собирать вычисления в цепочки.
bool combination()
{
   if(combine(func1(0.5), func1(0,3), func1(0,7))
   {
     Print("Gotcha!!");
     return true;
   }else
   {
       return false;
   }
}

Естественно, combine() может перегружаться как про параметрам, так и по возвращаемому значению.
И хотелось бы глядя на сигнатуру метода сразу понять, каким образом внутри нее работает combine.
То есть какой-то механизм по типу Checked Exceptions.
bool combination() overloads combine([double -> bool], bool)
{
   if(combine(func1(0.5), func1(0,3), func1(0,7))
   {
     Print("Gotcha!!");
     return true;
   }else
   {
       return false;
   }
}

То бишь в этом примере combine принимает список функций принимающих на вход дабл и возвращающих булевское значение.
Заметь что способ, каким именно образом вычисляется цепочка зависит от внутренней реализации combine. Это может быть как стратегия работаем до первого false, так и, допустим, true и false должны чередоваться, или даже 3 false, 1 true, 5 false.
Такой механизм дает практически неограниченные возможности по комбинации вычислений. При этом вычисления могут быть любыми.
Хатишь допустим, чтобы перед вызовом следующей функции в цепочке скидывались в лог ее параметры — леххко! Переопределил combine и вуаля.
Или у нас есть цепочка селектов из БД, и если на одном этапе запрос вернул пустое множество, то продолжать дальше вычисления не имеет смысла. (LINQ, ага)
Или допустим чтобы запускалась ракета земля-земля, или... you've got the idea...

можно даже сдлеать несколько наиболее распространенных способов комбинации вычислений и запихнуть их в стандартную библиотеку. Что кстати и было сделано.
Так что монады это всего навсего кучка готовых реализаций combine, которые позволяют комбинировать разные вычисления разными способами.

Теперь по поводу ввода вывода.
Для ввода-вывода combine это таки да. Грязная императивщина. Это гарантия того, что функции внутри IO combine будут выполнятся строго последовательно.
Но точно так же можно было бы сделать Parallel combine, где функции бы вызывались в разных потоках. Что кстати в F# и было сделано.
async
{
    let res1 = func1();
    let res2 = func2();
}


Неужто последовательное выполнение функций это изврат и мозголомство а параллельный запуск благо и ТруЪ ?

VD>Везде есть два подхода 1) прагматичный и 2) фанатичный. На мой взгляд прагматичный подход в данном вопросе признаться себе, что весь мир в доску императивен, и только отдельные его части можно представить как чистую математику. Вот эти части и надо рассматривать как функциональные вычисления, а к БД лучше подходить с императивным мировозрением.

+1 Ты не поверишь, но именно так и сделано. Там где у нас императив аля ввод-вывод или БД — стоит IO и функции выполняются строго последовательно.
И сразу видно что тут мухи — а тут котлеты. В смысле эти функции чисто ФПшные. а эти работают с IO. То есть грязный имератив.

VD>В общем, надо для себя решить является ли функциональное программирование самоцелью или это всего лишь способ облегчить решение задачи. Если второе, то ФП надо рассматривать как средство повышения декларативности в программах. Тогда SQL можно рассматривать как очень высокоуровневый, декларативный язык специально созданный для обработки данных в БД (и превосходящий любые универсальные языки), а стало быть более предпочтительный. Общение с внешним миром рассматривать как банальную императивную передачу данных.

Хотелось бы донести мысль. Монады можно использовать для реализации императивщины. Для этого они и используются. НО утверждать что они нужны исключительно для этого — неправильно.
Кстати еще вариант. Можешь рассматривать монаду IO как аналог unsafe{} блока в c#. То есть у нас везде все managed, но вот в этом конкретном месте к нам проникла опасная грязь.
Re[3]: Побочные эффекты и запись чтение в базу данных
От: Mr.Cat  
Дата: 04.05.08 21:38
Оценка:
Здравствуйте, Mirrorer, Вы писали про combine:

По мне для комбинации вычислений лучше подходят arrows.
Re[4]: Побочные эффекты и запись чтение в базу данных
От: Mirrorer  
Дата: 04.05.08 21:47
Оценка:
Здравствуйте, Mr.Cat, Вы писали:

MC>По мне для комбинации вычислений лучше подходят arrows.


Я ж не волшебник я только учусь (с)

Пост был очередной попыткой объяснть широкой публике цо то е — монады.

Кстати если где-то есть доступное общечеловеческим языком написанное введение в стрелки — прошу ссылки...
Re[5]: Побочные эффекты и запись чтение в базу данных
От: Mr.Cat  
Дата: 04.05.08 22:00
Оценка: 21 (2)
Здравствуйте, Mirrorer, Вы писали:
M>Кстати если где-то есть доступное общечеловеческим языком написанное введение в стрелки — прошу ссылки...

Я "вводился" по вот этой штуке: John Hughes &mdash; Programming with Arrows.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.