На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
  
> Сломать шаблон , функциональный подход vs традиционный ООП
    Люди умеющие работать с функциональными ЯП, объясните, как сломать шаблон правильно представить в функциональной парадигме то, что раньше было ООП?
    Например класс содержащий какие-то там поля-свойства и методы. Как этот объект представляется в функциональных языках?
    Посмотрим на Erlang, педивикия пишет, что можно задействовать кортеж, но можно ли обратиться к элементам кортежа по именам полей?
    Как реализовать всякие методы? Делать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C?
    Сообщение отредактировано: applegame -
      Цитата applegame @
      Делать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C?
      объект с полями и указателями на методы, если совсем кратко
        Цитата SVK @
        объект с полями и указателями на методы, если совсем кратко
        И как создать такой объект? Можно простейший пример на том же Erlang?
          Цитата applegame @
          1) Люди умеющие работать с функциональными ЯП, объясните, как сломать шаблон правильно представить в функциональной парадигме то, что раньше было ООП?
          2) Например, класс содержащий какие-то там поля-свойства и методы.
          3) Как этот объект представляется в функциональных языках?
          4) Посмотрим на Erlang, педивикия пишет, что можно задействовать кортеж, но можно ли обратиться к элементам кортежа по именам полей?
          5) Как реализовать всякие методы? Делать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C?

          1) В общем случае никак, это же другой подход, без объектов. В частных случаях — с помощью замыканий, например.
          2) Это не пример. Программирование существует не ради программирования, а объекты не ради объектов.
          3) См. пункт 1.
          4) У кортежа нет имён полей, не понимаю, о чём ты.
          5) См. пункт 1.

          Все ответы на твои вопросы есть в SICP:

          Lecture 6A

          Lecture 6B
          Сообщение отредактировано: korvin -
            Цитата SVK @
            Цитата applegame @
            Делать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C?
            объект с полями и указателями на методы, если совсем кратко

            Ты функциональное программирование с процедурным не путаешь?
            Какие указатели в функциональном программировании?

            Добавлено
            applegame, может начать со scala/f#, просто оставить классы на какое-то время и посмотреть, к чему в итоге придёт проект?

            Добавлено
            Цитата applegame @
            Например класс содержащий какие-то там поля-свойства и методы. Как этот объект представляется в функциональных языках?

            В функциональных языках лучше это никак не представлять, а спроектировать другое решение исходной задачи.
              Цитата korvin @
              4) У кортежа нет имён полей, не понимаю, о чём ты.
              Это из педивикии, что-то я не так понял видимо:
              Цитата Wikipedia..Erlang.Кортеж
              В Erlang принято строить различные типы данных на основе кортежей с тегами, что облегчает отладку программы и считается хорошим стилем программировани
              Что такое кортеж, объяснять не нужно.
              Цитата korvin @
              1) В общем случае никак, это же другой подход, без объектов. В частных случаях — с помощью замыканий, например.

              Цитата D_KEY @
              В функциональных языках лучше это никак не представлять, а спроектировать другое решение исходной задачи.

              Я об этом, по сути, и спрашиваю. Смотришь на всякие учебники для новичков, а там туфта вроде подсчета чисел Фибоначчи, сортировки списков, всяких map/reduce, которые есть в любом современном языке и нихрена функциональному подходу не учат.
              Поэтому и говорю о ломании шаблона. Я при проектировании задачи сразу начинаю мыслить объектами. Как от этого уйти?
              Допустим простейший игровой сервер, где есть карта, есть NPC бродящие по этой карте и игроки. Ну вот как это спроектировать без ООП, ума не приложу?
              Ну карта - двумерный массив, а мобы и игроки?

              Добавлено
              Цитата D_KEY @
              Ты функциональное программирование с процедурным не путаешь?
              Какие указатели в функциональном программировании?
              Да, я тоже подумал что что-то недопонято. Я ковырял в свое время игровой сервер написанный целиком на C. Но там один хрен было ООП, как раз через поля структур в виде указателей. Там даже было наследование этих структур сымитировано через касты.
                D_KEY: да, попутал %)
                  Цитата korvin @
                  Все ответы на твои вопросы есть в SICP:
                  А на русском есть что-нибудь подобное. Я тяжело воспринимаю английский на слух.

                  Добавлено
                  Вот нашел первое попавшееся - http://www.youtube.com/watch?v=Ze6qdlyz-30 буду посмотреть.
                    Цитата applegame @
                    Цитата korvin @
                    Все ответы на твои вопросы есть в SICP:
                    А на русском есть что-нибудь подобное. Я тяжело воспринимаю английский на слух.

                    Ну книга SICP переведена на русский("структура и имплементация компьютерных программ"). Но я бы тебе ее рекомендовать не стал... Она слишком базовая. В MIT этот курс был для CS, а не SE.

                    Так что на счёт использования для начала функциональных языков, которые умеют ООП? Ocaml, F#, Scala. А там постепенно где видишь пользу от функционального программирования, там и используешь. Мне вообще кажется, что эти подходы вполне себе существуют вместе, на более высоком уровне у нас ОО-система, а на более детальном уровне функциональщина("конвейерная" работа с потоками данных, как можно меньше мутабельности, простое распараллеливание, сопоставление с образцом, алгебраические типы данных и пр.).

                    Добавлено
                    Цитата applegame @
                    Поэтому и говорю о ломании шаблона. Я при проектировании задачи сразу начинаю мыслить объектами. Как от этого уйти?
                    Допустим простейший игровой сервер, где есть карта, есть NPC бродящие по этой карте и игроки. Ну вот как это спроектировать без ООП, ума не приложу?

                    На последнем докладе о функциональщине, что я видел(вживую), докладчик рассматривал пример игры "жизнь". А потом спел песню про монады :D

                    Ссылку могу дать чуть позднее. Там на русском.

                    Добавлено
                    Agile Days 2015, Вагиф Абилов: А нам-то зачем функциональное программирование?
                    Цитата
                    В докладе на простых примерах дается представление о том, чем отличается моделирование предметной области и реализация функционала при использовании функциональных языков - таких, как F#, Scala и Clojure, в сравнении с объектно-ориентированными C# и Java. Какие типы задач наиболее подходят для функциональных языков? Как их лучше внедрять в проект? И как убедить руководство в практичности такого выборы? Обо всем этом пойдет речь в докладе.


                    Ну рассказал он не обо всем. Зато спел :)
                      Цитата applegame @
                      Это из педивикии, что-то я не так понял видимо:
                      Цитата Wikipedia..Erlang.Кортеж
                      В Erlang принято строить различные типы данных на основе кортежей с тегами, что облегчает отладку программы и считается хорошим стилем программировани
                      Что такое кортеж, объяснять не нужно.

                      А, кортеж с тэгами, это другое дело. По сути это записи (records) =)
                      Это уже зависит от конкретного языка, в Хаскелле, например, получить значения полей можно с помощью одноимённых функций-селекторов, которые автоматически генерируются при объявлении типа-записи. Не очень удобное решение, получается невозможно определить в одном модуле два типа-записи с одноимённым полем, будет конфликт имён. В Ocaml записи получше сделаны, ИМХО. Какой синтаксис в Erlang'е для работы с такими типами — ХЗ. Ну и конечно сопоставление с образцом (pattern matching), если у тебя не opaque-тип.

                      Цитата applegame @
                      Я об этом, по сути, и спрашиваю. Смотришь на всякие учебники для новичков, а там туфта вроде подсчета чисел Фибоначчи, сортировки списков, всяких map/reduce, которые есть в любом современном языке и нихрена функциональному подходу не учат.

                      Почитай Real World Haskell или Real World Ocaml (правда в Ocaml есть и классы с объектами, и сайд-эффекты можно использовать свободно, без контроля со стороны системы типов, поэтому Хаскелл в целом будет более нагляден).
                      Там более практичные примеры.

                      Цитата applegame @
                      Поэтому и говорю о ломании шаблона. Я при проектировании задачи сразу начинаю мыслить объектами. Как от этого уйти?
                      Допустим простейший игровой сервер, где есть карта, есть NPC бродящие по этой карте и игроки. Ну вот как это спроектировать без ООП, ума не приложу?
                      Ну карта - двумерный массив, а мобы и игроки?

                      Записи.
                      Сообщение отредактировано: korvin -
                        Цитата D_KEY @
                        Так что на счёт использования для начала функциональных языков, которые умеют ООП? Ocaml, F#, Scala. А там постепенно где видишь пользу от функционального программирования, там и используешь. Мне вообще кажется, что эти подходы вполне себе существуют вместе, на более высоком уровне у нас ОО-система, а на более детальном уровне функциональщина("конвейерная" работа с потоками данных, как можно меньше мутабельности, простое распараллеливание, сопоставление с образцом, алгебраические типы данных и пр.).

                        Agile Days 2015, Вагиф Абилов: А нам-то зачем функциональное программирование?
                        Цитата
                        В докладе на простых примерах дается представление о том, чем отличается моделирование предметной области и реализация функционала при использовании функциональных языков - таких, как F#, Scala и Clojure, в сравнении с объектно-ориентированными C# и Java. Какие типы задач наиболее подходят для функциональных языков? Как их лучше внедрять в проект? И как убедить руководство в практичности такого выборы? Обо всем этом пойдет речь в докладе.


                        Ну рассказал он не обо всем. Зато спел :)
                        Спасибо за ссылку, интересный и достаточно веселый доклад, хотя нового оттуда я мало чего почерпнул, скорее слегка углубил, то что уже знал. Кроме монад конечно. Я еще ими серьезно не занимался, а из доклада так и не понял разницу между исключениями и монадами. Также не совсем понял, что он там говорил об описании неких доменов. Это надо отдельно читать.
                        В качестве небольшого упражнения переписал F# код для игры Жизнь из лекции Абилова на D (фактически портирование):
                        Игра жизнь на D в функциональном стиле
                        ExpandedWrap disabled
                          import std.range;
                          import std.typecons;
                          import std.algorithm;
                          import std.functional;
                          import std.container;
                           
                          // возвращает список соседей клетки
                          auto neighbours(C)(C cell) {
                              return iota(-1,2)
                                  .map!(i => iota(-1,2)
                                      .map!(j => tuple(cell[0]+i, cell[1]+j)))
                                  .joiner
                                  .filter!(c => c != cell);
                          }
                           
                          // определяет жива ли клетка (клетка жива, если она есть в популяции)
                          bool isAlive(P, C)(P population, C cell) {
                              return population
                                  .canFind(cell);
                          }
                           
                          // возвращает список живых соседей клетки
                          auto aliveNeighbours(P, C)(P population, C cell) {
                              return cell
                                  .neighbours
                                  .filter!(curry!(isAlive, population));
                          }
                           
                          // определяет выживет ли клетка в следующей итерации
                          bool survives(P, C)(P population, C cell) {
                              return population
                                  .aliveNeighbours(cell)
                                  .walkLength.among(2, 3) > 0;
                          }
                           
                          // определяет родится ли клетка в следующей итерации
                          bool reproducible(P, C)(P population, C cell) {
                              return population
                                  .aliveNeighbours(cell)
                                  .walkLength == 3;
                          }
                           
                          // возвращает список всех "мертвых" соседей всех клеток популяции
                          auto allDeadNeighbours(P)(P population) {
                              return population
                                  .map!neighbours
                                  .joiner
                                  .make!RedBlackTree[]
                                  .filter!(not!(curry!(isAlive, population)));
                          }
                           
                          // возвращает следующее поколение популяции
                          auto nextGeneration(P)(P population) {
                              // соединяем списки всех выживших и родившихся клеток
                              return chain(
                                  population
                                      .filter!(curry!(survives, population)),
                                  population.allDeadNeighbours
                                      .filter!(curry!(reproducible, population))
                              );
                          }
                           
                          void main() {
                              import std.stdio;
                              import std.typecons;
                              immutable population = [tuple(-1,0), tuple(0,0), tuple(1,0)];
                              population.nextGeneration.writeln;
                          }

                        В целом, функциональным подходом на уровне вышеприведенного кода я и так активно пользуюсь в своих проектах, включая иммутабельность (как раз ради эффективного распараллеливания без синхронизации между потоками).
                        С игрой Жизнь все понятно, вычичление новой итерации - чистая функция. Получили на вход список, вернули новый список. Результат строго детерминирован от входных данных.
                        Но вот типа на Erlang пишут распределенные системы, веб-сервера и все такое. В веб-сервере запросы зачастую не могут быть чистыми функциями, например запись в БД - вполне себе сайд-эффект. Что-то мне подсказывает что тут-то и начинается расколбас с монадами. Это так?

                        Добавлено
                        Еще вот интересный момент: у Абилова в примере с игрой Жизнь, в функции создания списка соседей присутствуют циклы. Разве в труЪ функциональном подходе циклы в традиционном понимании используются?
                        Сообщение отредактировано: applegame -
                          Цитата applegame @
                          1) Но вот типа на Erlang пишут распределенные системы, веб-сервера и все такое. В веб-сервере запросы зачастую не могут быть чистыми функциями, например запись в БД - вполне себе сайд-эффект. Что-то мне подсказывает что тут-то и начинается расколбас с монадами. Это так?

                          2) Еще вот интересный момент: у Абилова в примере с игрой Жизнь, в функции создания списка соседей присутствуют циклы. Разве в труЪ функциональном подходе циклы в традиционном понимании используются?

                          1) Монады как бы не при чём. В Clean, нет монад, например, для сайд-эффектов используются unique types (или как-то так). Монада (в Хаскелле) — это всего лишь интерфейс, определяющий некоторые методы работы с данными. Если проводить аналогии с реальным миром, то это такая коробка, положив какой-то объект в которую, ты не можешь просто достать его, ты можешь только достать объект, что-то с ним сделать и положить в такую же коробку. Монада IO в этом плане несколько особенная, хотя язык не регламентирует её реализацию в исполнителе, но именно IO используется исполнителем, для реализации взаимодействия с внешним миром.

                          В Erlang'е монад нет, а интерфейс работы с БД может выглядеть и использоваться вполне себе функционально, ведь все сайд-эффекты как бы находятся во внешнем мире. «let (rows, err) = db.select(query, param...) in ...» — выглядит вполне себе функционально. «let err = db.exec("UPDATE ...", param...) in ...» тоже.

                          2) Не используются. Но синтаксис может позволять их выразить стандартными средствами, например forM_ в Хаскелле выглядит (и используется) как вполне себе цикл.

                          Добавлено
                          Вот тут, кстати, чел приводит пример, как в Хаскелле «реализуется ООП»: http://stackoverflow.com/questions/1310668...110560#13110560

                          Можешь ещё посмотреть на Functional Reactive Programming в Хаскелле, которое применяется для GUI, который обычно считается очень ООП-шной предметной областью.
                            Цитата korvin @
                            В Erlang'е монад нет, а интерфейс работы с БД может выглядеть и использоваться вполне себе функционально, ведь все сайд-эффекты как бы находятся во внешнем мире. «let (rows, err) = db.select(query, param...) in ...» — выглядит вполне себе функционально. «let err = db.exec("UPDATE ...", param...) in ...» тоже.
                            Да, но ведь такая функция получается не "чистая".

                            Ну хорошо. Прочитал вот тут про монаду состояния. Автор сэмулировал ее в жабаскрипте. Меня удивило, что ничего особенно сложного (по крайней мере в этом типе монад) я не заметил. Возможно я что-то тотально не понял и мне только показалось, что все понятно. И вот я сделал аналогичный код на D для того же стека на базе массива. Можно ли считать, что я сделал аналог монады состояния или все же, что-то было глобально не понято? Функции push и pop каррированы вручную, хотя в D для это есть бибилиотечная функция.
                            http://dpaste.dzfl.pl/bcf704330224
                            ExpandedWrap disabled
                              import std.stdio;
                               
                              auto push(int value) {
                                  return (const int[] stack) => stack ~ value;
                              }
                              auto pop() {
                                  return (const int[] stack) => stack.dup[0..$-1];
                              }
                               
                              auto monad(FS...)(FS funcs) if(funcs.length > 1) {
                                  return (const int[] stack) => monad(funcs[1..$])(funcs[0](stack));
                              }
                              auto monad(FS...)(FS funcs) if(funcs.length == 1) {
                                  return (const int[] stack) => funcs[0](stack);
                              }
                               
                              void main() {
                                  auto computation = monad(
                                      push(5),
                                      push(3),
                                      pop
                                  );
                                  computation([1, 2]).writeln;
                              }
                            Сообщение отредактировано: applegame -
                              Смотри как бы у тебя не развился D головного мозга ;) Все зачем-то на D переводишь. Это скорее вредно при попытках разобраться в чем-либо.
                              Сообщение отредактировано: D_KEY -
                                Цитата applegame @
                                1) Да, но ведь такая функция получается не "чистая".

                                2) Ну хорошо. Прочитал вот тут про монаду состояния. Автор сэмулировал ее в жабаскрипте. Меня удивило, что ничего особенно сложного (по крайней мере в этом типе монад) я не заметил. Возможно я что-то тотально не понял и мне только показалось, что все понятно. И вот я сделал аналогичный код на D для того же стека на базе массива. Можно ли считать, что я сделал аналог монады состояния или все же, что-то было глобально не понято? Функции push и pop каррированы вручную, хотя в D для это есть бибилиотечная функция.
                                http://dpaste.dzfl.pl/bcf704330224
                                ExpandedWrap disabled
                                  import std.stdio;
                                   
                                  auto push(int value) {
                                      return (const int[] stack) => stack ~ value;
                                  }
                                  auto pop() {
                                      return (const int[] stack) => stack.dup[0..$-1];
                                  }
                                   
                                  auto monad(FS...)(FS funcs) if(funcs.length > 1) {
                                      return (const int[] stack) => monad(funcs[1..$])(funcs[0](stack));
                                  }
                                  auto monad(FS...)(FS funcs) if(funcs.length == 1) {
                                      return (const int[] stack) => funcs[0](stack);
                                  }
                                   
                                  void main() {
                                      auto computation = monad(
                                          push(5),
                                          push(3),
                                          pop
                                      );
                                      computation([1, 2]).writeln;
                                  }

                                1) Она нечиста лишь потому, что нечиста БД, с точки зрения языка она относительно чиста, т.к. не позволяет изменять значения переменных. Ну в Хаскелле для того и существует IO, а в Эрланге с его динамической типизацией… Хз, возможно и можно было помечать все нечистые функции как-нибудь и сделать эту пометку «заразной», но зачем? Эрланг больше про взаимодействие процессов, чем функциональщину, а процессы выполняются параллельно, т.е. в любом случае получаем нечистую систему, просто описывается она языком в основном чистых функций. Так что Эрланг — далеко не самый удачный язык для знакомства с ФП, лучше взять Хаскелл, Clean, Miranda (последний, правда, давно мёртв).

                                2) Ты ничего не понял, я же написал, что Монада (в Хаскелле) — это просто интерфейс или абстрактный класс с парой методов (на самом деле там их вроде четыре, но обычно определяют два — return и (>>=), т.к. (=<<), (>>) и (<<) выражены через (>>=), а fail определяется автоматически или что-то в таком духе).

                                Т.е. на «традиционном ООП языке» Монада выглядит так:

                                ExpandedWrap disabled
                                  // Monad lib
                                   
                                  public interface Monad forall<A, B> {
                                      Monad<B> bind(M<B> proc(A));
                                  }
                                   
                                  // Monad IO lib
                                   
                                  import Monad;
                                   
                                  public class IO<A> : Monad forall<B> {
                                   
                                      private A value;
                                   
                                      // конструктор выполняет роль функции join (она же return в Хаскелльной Monad)
                                      public IO(A value) {
                                          this.value = value;
                                      }
                                   
                                      public IO<B> bind(IO<B> proc(A)) {
                                          return proc(this.value);
                                      }
                                  }
                                   
                                  // IO utilities lib
                                   
                                  import IO;
                                   
                                  public IO<Void> print(T value) {
                                      Console.WriteLine(value);
                                      return new IO<Void>();
                                  }
                                   
                                  public IO<T> read() {
                                      return new IO<T>(Console.Read<T>());
                                  }
                                   
                                  // Our code
                                   
                                  import IO, IOutil;
                                   
                                  Int inc(Int x) {
                                      return x + 1;
                                  }
                                   
                                  IO<Int> ioInc(Int x) {
                                      return new IO<Int>(x + 1);
                                  }
                                   
                                  Void main() {
                                      IO<Int> x = read<Int>();
                                      // теперь мы не можем просто взять и вытащить x из монады IO
                                      // и не можем использовать простую inc, только ioInc
                                      x
                                      .bind(ioInc)
                                      .bind(print);
                                      // важно то, что результатом любой манипуляции с IO-значением
                                      // является (другое или такое же) IO-значение, т.е.
                                      // монада как бы "заразна". Вот и всё.
                                  }

                                Это упрощённо, без ленивости.

                                Добавлено
                                Цитата applegame @
                                Автор сэмулировал ее в жабаскрипте.

                                Совершенно, абсолютно бесполезное занятие, особенно для объяснения, что такое монада. Автор вообще не понял, что это, смешал в кучу монаду, ST и ленивость. Зачем? Монада IO в JS, да и в (почти) любом императивном языке — это точка с запятой ( ; ), точнее это функция bind, именно она позволяет компилятору выстроить правильную последовательность действий в программе.
                                Сообщение отредактировано: korvin -
                                  Небольшое уточнение: «монада как бы "заразна"» — это касается монады IO (и, вероятно, некоторых других), т.к. Монада — это класс типа, а не сам тип, она не регулирует конструирование значений типа и не запрещает наличие функций, вытаскивающих значение из монадного типа, т.е. невозможность «вытащить» значение из монады произвольным способом обеспечивается модулем, в котором тип определён. Например модуль, в котором определён тип ST, предоставляет функцию runST, вытаскивающую значение из монады ST, т.к. ST — чистый тип, без сайд-эффектов. Монаду можно определить даже для списков, чтобы воспользоваться do-нотацией:

                                  ExpandedWrap disabled
                                    import Control.Monad
                                     
                                    foo n = do
                                        i <- [1..n]
                                        j <- [i..n]
                                        k <- [j..n]
                                        return (i, j, k)
                                     
                                    bar n = [ (i, j, k) | i <- [1..n], j <- [i..n], k <- [j..n] ]
                                     
                                    baz n = concat $ concat $
                                        map (\i -> map (\j -> map (\k -> (i, j, k))
                                                                  [j..n])
                                                       [i..n])
                                            [1..n]
                                     
                                    main = let n = 3 in
                                        forM_ [foo, bar, baz] $ \f ->
                                            print $ f n


                                  ExpandedWrap disabled
                                    [(1,1,1),(1,1,2),(1,1,3),(1,2,2),(1,2,3),(1,3,3),(2,2,2),(2,2,3),(2,3,3),(3,3,3)]
                                    [(1,1,1),(1,1,2),(1,1,3),(1,2,2),(1,2,3),(1,3,3),(2,2,2),(2,2,3),(2,3,3),(3,3,3)]
                                    [(1,1,1),(1,1,2),(1,1,3),(1,2,2),(1,2,3),(1,3,3),(2,2,2),(2,2,3),(2,3,3),(3,3,3)]

                                  http://ideone.com/E21ZDr

                                  Как нетрудно заметить, функции foo, bar и baz абсолютно идентичны по результату и отличаются лишь во внешнем виде исходного кода. foo реализована с помощью do-нотации благодаря Monad [].

                                  https://www.fpcomplete.com/school/starting-...-the-list-monad
                                  http://en.wikibooks.org/wiki/Haskell/Under...ing_monads/List
                                    Цитата D_KEY @
                                    Смотри как бы у тебя не развился D головного мозга ;)
                                    Уже развился до терминальной стадии. :D Но это не ослепляет меня. В D полно говна, но пока это лучшее, из того, что у меня есть.
                                    Цитата D_KEY @
                                    Все зачем-то на D переводишь. Это скорее вредно при попытках разобраться в чем-либо.
                                    На D потому что в данный момент это язык, которым я владею намного лучше остальных языков. Я мог бы ваять примеры на Ruby или плюсах, но это заняло бы гораздо больше времени.
                                    Цитата korvin @
                                    Совершенно, абсолютно бесполезное занятие, особенно для объяснения, что такое монада. Автор вообще не понял, что это, смешал в кучу монаду, ST и ленивость. Зачем? Монада IO в JS, да и в (почти) любом императивном языке — это точка с запятой ( ; ), точнее это функция bind, именно она позволяет компилятору выстроить правильную последовательность действий в программе.

                                    Насчет точки с запятой - очень хорошая аналогия. помогла мне кое-что осознать. С точки зрения функционального программирования не существует алгоритма, как некоей последовательности действий. Это, ИМХО, крайне важный момент, которому, я считаю, уделяют слишком мало внимания в лекциях и учебниках. Сама природа человека строит все на цепочках событий, а тут надо забыть про это. Есть исходный материал, а есть множество функций от этого материала и множество результатов этих функций. Причем и материалы и результаты также могут быть функциямии. Все это происходит вне времени. Время конечно может участвовать как агрумент функций, но сами функции "плавают" в некоем безвременном "гиперпространстве".
                                    Суть монады, насколько я понял, как раз в формализации последовательности из двух вычислений. При этом значения аргумента и результата обернуты в монаду, а результат первого вычисления является аргументом второго вычисления.
                                    Своим "примером" монады, я скорее сымитировал один из вариантов реализации внутренностей монады, а не саму монаду.
                                      Цитата applegame @
                                      Насчет точки с запятой - очень хорошая аналогия. помогла мне кое-что осознать. С точки зрения функционального программирования не существует алгоритма, как некоей последовательности действий. Это, ИМХО, крайне важный момент, которому, я считаю, уделяют слишком мало внимания в лекциях и учебниках. Сама природа человека строит все на цепочках событий, а тут надо забыть про это. Есть исходный материал, а есть множество функций от этого материала и множество результатов этих функций. Причем и материалы и результаты также могут быть функциямии. Все это происходит вне времени. Время конечно может участвовать как агрумент функций, но сами функции "плавают" в некоем безвременном "гиперпространстве".

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

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

                                      А суть монады ты неправильно понял, как и тот автор, я ж написал пример на предыдущей странице. У монады нет никаких внутренностей, это интерфейс.
                                        Цитата korvin @
                                        Это всё пустые философствования. Ты не можешь вернуться в прошлое, например, таким образом пространство-время — это просто поток, ленивый список моментов, вычисляющийся по мере необходимости (течении времени), преобразование одного момента в другой — чистая функция. Но что толку от всех этих аналогий?
                                        Философствования - да. Пустые ли они? Не уверен. Для проектирования задачи важно понимание. Перестать мыслить в рамках последовательности действий и начать мыслить в рамках функциональных трансформаций. Фиг его знает, мне помогает.
                                        Цитата korvin @
                                        А суть монады ты неправильно понял, как и тот автор, я ж написал пример на предыдущей странице. У монады нет никаких внутренностей, это интерфейс.
                                        Странно. У абстрактной монады, конечно, нет никаких внутренностей, но как же конкретные реализации вроде монады Maybe? Разве у нее нет внутренностей? Даже в твоем примере у класса IO<A> вполне есть "внутренности": join и bind.
                                        Сообщение отредактировано: applegame -
                                          Цитата applegame @
                                          Странно. У абстрактной монады, конечно, нет никаких внутренностей, но как же конкретные реализации вроде монады Maybe? Разве у нее нет внутренностей? Даже в твоем примере у класса IO<A> вполне есть "внутренности": join и bind.

                                          Странно, у интерфейса Monad, конечно, нет никаких внутренностей, но как же конкретные реализации вроде класса IO? Разве у класса нет внутренностей?

                                          Я не понимаю, в чём твоя проблема, в этом вашем D нет интерфейсов и классов?
                                            Цитата korvin @
                                            Я не понимаю, в чём твоя проблема, в этом вашем D нет интерфейсов и классов?
                                            Проблем никаких нет. Ответ ты и сам знаешь.
                                              Попрбовал Haskell и OCaml.
                                              Первый кошерен, второй мерзок.
                                              Кому пришла в голову бредовая идея делать отдельные математические операторы для int и float? Разделение элементов списка точками с запятой? Фу короче.
                                              А вот Хаскель - приятная штука.
                                              Сообщение отредактировано: applegame -
                                                В хаскеле проблема ad-hoc полиморфизма просто решена здраво через type class'ы. В ocaml так просто не сделаешь, потому и отдельные операции там, где это важно с точки зрения типизации. Вместо ocaml можешь попробовать F#, это почти тот же язык, только причесанный и с многими дополнительными плюшками.
                                                  Цитата D_KEY @
                                                  Вместо ocaml можешь попробовать F#, это почти тот же язык, только причесанный и с многими дополнительными плюшками.
                                                  Он неплох, я уже немного видел его в приведеном тобой ролике с песней о монадах. Если бы не его привязанность к .NET, может быть и поковырялся бы с ним.
                                                  Пока поизучаю Haskell, тем более что многие вещи оказались знакомы по D (D_LANG гойловного мозга налицо). Например, бесконечные ленивые списки == бесконечные ленивые ranges.
                                                  Сообщение отредактировано: applegame -
                                                    Цитата applegame @
                                                    1) Кому пришла в голову бредовая идея делать отдельные математические операторы для int и float?

                                                    2) Разделение элементов списка точками с запятой? Фу короче.

                                                    3) А вот Хаскель - приятная штука.

                                                    1) Тому, кто придумал строгую типизацию и вывод типов. ML был придуман в 70-х, когда Хаскелла с его тайпклассами даже в проекте не было. ML полностью отрицает неявное приведение типов.

                                                    2) Вкусовщина. Мне не нравится, что имена типов пишутся со строчной буквы и странные правила использования двойной точки-с-запятой, но это такая мелочь.

                                                    3) Да, но мне интересно, что ты скажешь, когда познакомишься с ним получше и узнаешь, что, например, написав хвостово-рекурсивную функцию внезапно обнаружишь переполнение стека, из-за ленивости, и окажется, что нужно форсировать вычисления специальным образом. Или то, что ты не можешь просто взять и вставить в середину функции отладочную печать, т.к. IO заразна и тебе придётся менять тип своей чистой функции на (IO T) (а то и переписывать её полностью). Несомненно, в дефолтной всеобщей ленивости и заразном IO есть свои плюсы, но зачастую это слегка непрактично.

                                                    Да и Функторы Ocaml немного удобней, понятней и проще в использовании, чем тайпклассы Хаскелла. + Если ты захочешь нормальный и удобный «динамический полиморфизм», ты скорее всего поменяешь своё мнение в пользу Ocaml.

                                                    Ну или F#, да, хотя, несмотря на некоторые весьма годные улучшения по сравнению с Ocaml, вроде есть и минусы. Если не ошибаюсь, модули например в F# примитивней Ocaml'овских.

                                                    Я поначалу Ocaml как-то тоже недолюбливал после Хаскелла, но потом, чем больше знакомился с ним, тем больше он мне стал нравиться (не в ущерб Хаскеллу, конечно =)).

                                                    Добавлено
                                                    Цитата applegame @
                                                    Например, бесконечные ленивые списки == бесконечные ленивые ranges.

                                                    Гм… А как будет на D'шных рейнджах выглядеть такой код:

                                                    ExpandedWrap disabled
                                                      fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
                                                       
                                                      main = print $ take 10 fibs

                                                    ExpandedWrap disabled
                                                      [0,1,1,2,3,5,8,13,21,34]


                                                    В Хаскелле ленивы не только списки, он ленив весь.
                                                    Сообщение отредактировано: korvin -
                                                      Цитата korvin @
                                                      Или то, что ты не можешь просто взять и вставить в середину функции отладочную печать, т.к. IO заразна и тебе придётся менять тип своей чистой функции на (IO T) (а то и переписывать её полностью).

                                                      А разве там нет специального костыля с unsafePerformIO как раз для дебажного принта?

                                                      Цитата korvin @
                                                      Гм… А как будет на D'шных рейнджах выглядеть такой код:

                                                      Ну в расте fibs будет возвращать итератор. Подозреваю, что рейнжи в D работают точно так же.
                                                      Сообщение отредактировано: DarkEld3r -
                                                        Цитата korvin @

                                                        Гм… А как будет на D'шных рейнджах выглядеть такой код:

                                                        ExpandedWrap disabled
                                                          fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
                                                           
                                                          main = print $ take 10 fibs

                                                        ExpandedWrap disabled
                                                          [0,1,1,2,3,5,8,13,21,34]


                                                        Аналогичный бесконечный ленивый рендж для чисел Фибоначчи можно сделать вот так:
                                                        ExpandedWrap disabled
                                                          import std.range;
                                                          import std.stdio;
                                                           
                                                          void main() {
                                                              auto fibs = recurrence!((a, n) => a[n-1] + a[n-2])(0, 1);
                                                              writeln(fibs.take(10));
                                                          }

                                                        ExpandedWrap disabled
                                                          (a,n) => a[n-1] + a[n-2] // лямбда вычисляющая очередное число
                                                          (0, 1)                   // первые два числа Фибоначчи

                                                        Результат - http://dpaste.dzfl.pl/9850c213f69a
                                                        Цитата korvin @
                                                        В Хаскелле ленивы не только списки, он ленив весь.

                                                        D по умолчанию не ленив, но ленивость можно указать явно:
                                                        ExpandedWrap disabled
                                                          import std.stdio;
                                                           
                                                          int foo() {
                                                              writefln("foo");
                                                              return 5;
                                                          }
                                                           
                                                          void bar(bool flag, lazy int data) { // аргумент data - ленивый
                                                              writefln("flag = %s", flag);
                                                              if(flag) writefln("data = %s", data);
                                                          }
                                                           
                                                          void main() {
                                                              bar(true, foo());
                                                              bar(false, foo());
                                                          }

                                                        ExpandedWrap disabled
                                                          flag = true
                                                          foo
                                                          data = 5
                                                          flag = false
                                                          Цитата DarkEld3r @
                                                          1) А разве там нет специального костыля с unsafePerformIO как раз для дебажного принта?

                                                          2) Ну в расте fibs будет возвращать итератор. Подозреваю, что рейнжи в D работают точно так же.

                                                          1) Есть такое выражение: «если вы используете UnsafePreformIO, то вам не нужен Haskell» или как-то так.

                                                          2) Я жду код, а не рассуждения.

                                                          Добавлено
                                                          Цитата applegame @
                                                          D по умолчанию не ленив, но ленивость можно указать явно:
                                                          ExpandedWrap disabled
                                                            import std.stdio;
                                                             
                                                            int foo() {
                                                                writefln("foo");
                                                                return 5;
                                                            }
                                                             
                                                            void bar(bool flag, lazy int data) { // аргумент data - ленивый
                                                                writefln("flag = %s", flag);
                                                                if(flag) writefln("data = %s", data);
                                                            }
                                                             
                                                            void main() {
                                                                bar(true, foo());
                                                                bar(false, foo());
                                                            }

                                                          ExpandedWrap disabled
                                                            flag = true
                                                            foo
                                                            data = 5
                                                            flag = false

                                                          Тут-то D и сел в лужу. Ибо сочетать ленивость и сайд-эффекты — это не в тапки гадить.

                                                          http://ideone.com/A0pXvm

                                                          ExpandedWrap disabled
                                                            foo = do
                                                                putStrLn "foo"
                                                                return 5
                                                             
                                                            bar flag d = do
                                                                putStrLn $ "flag = " ++ show flag
                                                                x <- d
                                                                if flag
                                                                    then putStrLn $ "data = " ++ show x
                                                                    else return ()
                                                             
                                                            main = do
                                                                bar True  foo
                                                                bar False foo


                                                          ExpandedWrap disabled
                                                            flag = True
                                                            foo
                                                            data = 5
                                                            flag = False
                                                            foo
                                                            korvin, раскрой мысль. Не очень понял, почему второй раз правильно вызывать foo. В чем фейл D?

                                                            Добавлено
                                                            И вообще, если ты сделаешь x <- d внутри if'а, то будет так же, как в D :-?
                                                            Сообщение отредактировано: D_KEY -
                                                              Цитата korvin @
                                                              Тут-то D и сел в лужу. Ибо сочетать ленивость и сайд-эффекты — это не в тапки гадить.
                                                              Твой код не эквивалентен моему.
                                                              Цитата D_KEY @
                                                              И вообще, если ты сделаешь x <- d внутри if'а, то будет так же, как в D
                                                              Ага, korvin смухлевал. :D
                                                              Вот как должно было быть:
                                                              http://ideone.com/GO7ZhH
                                                              ExpandedWrap disabled
                                                                foo = do
                                                                    putStrLn "foo"
                                                                    return 5
                                                                 
                                                                bar flag d = do
                                                                    putStrLn $ "flag = " ++ show flag
                                                                    if flag
                                                                        then do
                                                                            x <- d
                                                                            putStrLn $ "data = " ++ show x
                                                                        else return ()
                                                                 
                                                                main = do
                                                                    bar True  foo
                                                                    bar False foo

                                                              ExpandedWrap disabled
                                                                flag = True
                                                                foo
                                                                data = 5
                                                                flag = False
                                                              Сообщение отредактировано: applegame -
                                                                Цитата applegame @
                                                                Аналогичный бесконечный ленивый рендж для чисел Фибоначчи можно сделать вот так:

                                                                D не знаю, так что интересно - take возвращает список или новый рейндж?

                                                                Цитата korvin @
                                                                1) Есть такое выражение: «если вы используете UnsafePreformIO, то вам не нужен Haskell» или как-то так.

                                                                Какая-то странная категоричность. Напрямую UnsafePreformIO использовать не надо, надо использовать Debug.Trace, который, вроде, из коробки есть. Ну и хочешь сказать это решение хуже, чем уродовать функции ради отладки?

                                                                Цитата korvin @
                                                                2) Я жду код, а не рассуждения.

                                                                Ну код уже есть и похоже D работает не так как я предположил. Мой вариант просто и хоть на плюсах изобразить можно. Хотя, конечно, это будет не прямой аналог. Смысл в следующем: fibs в расте вернёт итератор, который хранит в себе состояние и на котором можно бесконечно звать next, ну а take ограничит этот итератор.

                                                                Не думаю, что код что-то изменит.
                                                                  Цитата DarkEld3r @
                                                                  D не знаю, так что интересно - take возвращает список или новый рейндж?
                                                                  Список? Ты наверное имел в виду массив. Нет не список/масив, а новый рейндж. Тоже ленивый. Функции обработки рейнджей в D сделаны лениво везде, где можно сделать их лениво. В частности map и filter тоже возвращают ленивые рейнджи.
                                                                  Цитата DarkEld3r @
                                                                  Ну код уже есть и похоже D работает не так как я предположил. Мой вариант просто и хоть на плюсах изобразить можно. Хотя, конечно, это будет не прямой аналог. Смысл в следующем: fibs в расте вернёт итератор, который хранит в себе состояние и на котором можно бесконечно звать next, ну а take ограничит этот итератор.
                                                                  На самом деле в D все точно так же. recurrence вовзращает рейндж хранящий внутри состояние. Рейнджи в D не являются встроенными в язык - это библиотечные сущности. Это, конечно, работает не так как корвиновский список складывающийся с самим собой со смещением, но результат аналогичен.
                                                                  Сообщение отредактировано: applegame -
                                                                    Цитата applegame @
                                                                    Список? Ты наверное имел в виду массив.
                                                                    Это не принципиально.
                                                                    Меня как раз интересовало как раз "контейнер" там или "честный рейндж".

                                                                    Цитата applegame @
                                                                    На самом деле в D все точно так же. recurrence вовзращает рейндж хранящий внутри состояние.
                                                                    Понятно. Ну в расте, это всё-таки больше "итераторы". В смысле, вывести на печать (стандартными средствами) итератор нельзя. То есть между print и take надо или собирать значения в контейнер или организовывать цикл для прохода.
                                                                      Цитата DarkEld3r @
                                                                      В смысле, вывести на печать (стандартными средствами) итератор нельзя. То есть между print и take надо или собирать значения в контейнер или организовывать цикл для прохода.

                                                                      Странный он, этот ваш rust :(
                                                                        Цитата DarkEld3r @
                                                                        Понятно. Ну в расте, это всё-таки больше "итераторы". В смысле, вывести на печать (стандартными средствами) итератор нельзя. То есть между print и take надо или собирать значения в контейнер или организовывать цикл для прохода.
                                                                        Дешный writeln/writefln почти всеяден. Фактически типобезопасный printf.

                                                                        Ну вот, шёл, понимаешь, кругом красивая природа, солнышко и тут бац - вступил в говно foldl vs foldl'
                                                                        Если вдруг у вас переполнение стека, то вместо ленивого foldl воспользуйтесь более strict(строгим?) foldl'. WTF?
                                                                        На самом деле я, конечно, все понимаю: рекурсия, ленивость и все такое, но математическую красоту слегка опоносили. Это наверное как раз об этом:
                                                                        Цитата korvin @
                                                                        Да, но мне интересно, что ты скажешь, когда познакомишься с ним получше и узнаешь, что, например, написав хвостово-рекурсивную функцию внезапно обнаружишь переполнение стека, из-за ленивости, и окажется, что нужно форсировать вычисления специальным образом.
                                                                        Сообщение отредактировано: applegame -
                                                                          Цитата D_KEY @
                                                                          Странный он, этот ваш rust :(

                                                                          Почему странный? В С++ ведь тоже нельзя просто так в printf/cout пару итераторов передать.

                                                                          Написать функцию которая "проходится по итератору" в расте тоже проблем никаких нет. Аналога ostream_iterator из коробки и правда нет. Но тут как раз всё дело в том, что применение map, filter и других подобных функций возвращает новый ленивый итератор. Для того чтобы получить значения надо итерироваться, ну или собрать значения куда-то.

                                                                          Добавлено
                                                                          Цитата applegame @
                                                                          Дешный writeln/writefln почти всеяден. Фактически типобезопасный printf.

                                                                          В расте print! - макрос, так что тоже может много всего, но не работать с итераторами. Подозреваю, что это сознательное решение - мол мало ли что может скрываться за итератором и т.д. К тому же, рейнжи в расте не копируемые.
                                                                            Цитата applegame @
                                                                            Ну вот, шёл, понимаешь, кругом красивая природа, солнышко и тут бац - вступил в говно foldl vs foldl'
                                                                            Если вдруг у вас переполнение стека, то вместо ленивого foldl воспользуйтесь более strict(строгим?) foldl'. WTF?
                                                                            На самом деле я, конечно, все понимаю: рекурсия, ленивость и все такое, но математическую красоту слегка опоносили. Это наверное как раз об этом:
                                                                            Цитата korvin @
                                                                            Да, но мне интересно, что ты скажешь, когда познакомишься с ним получше и узнаешь, что, например, написав хвостово-рекурсивную функцию внезапно обнаружишь переполнение стека, из-за ленивости, и окажется, что нужно форсировать вычисления специальным образом.

                                                                            Нет, не об этом. Как раз наоборот, ленивые языки более «математически красивы» (что бы ты под этим не подразумевал), «уродствами» они становятся с другой, технической точки зрения.

                                                                            Добавлено
                                                                            Цитата applegame @
                                                                            Ага, korvin смухлевал. :D

                                                                            Не, просто поспешил.

                                                                            Добавлено
                                                                            Цитата DarkEld3r @
                                                                            Какая-то странная категоричность. Напрямую UnsafePreformIO использовать не надо, надо использовать Debug.Trace, который, вроде, из коробки есть. Ну и хочешь сказать это решение хуже, чем уродовать функции ради отладки?

                                                                            Трассировка не всегда именно то, что нужно. А сказать я хочу, что ленивость-по-умолчанию не всегда удобна.
                                                                              Цитата korvin @
                                                                              Нет, не об этом. Как раз наоборот, ленивые языки более «математически красивы» (что бы ты под этим не подразумевал), «уродствами» они становятся с другой, технической точки зрения.
                                                                              Какое-то непонимание. Полагаю, что мы говорили об одном и том же. Под "опоносили" я подразумевал не ленивость, а как раз необходимость всяких обходных маневров вроде специальных версий тех же самых функций. Я понимаю, что это вынужденная мера, но осадочек-то остается.
                                                                                Цитата applegame @
                                                                                Какое-то непонимание. Полагаю, что мы говорили об одном и том же. Под "опоносили" я подразумевал не ленивость, а как раз необходимость всяких обходных маневров вроде специальных версий тех же самых функций. Я понимаю, что это вынужденная мера, но осадочек-то остается.

                                                                                А, ну да, я это имел в виду. =)
                                                                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                                                0 пользователей:


                                                                                Рейтинг@Mail.ru
                                                                                [ Script execution time: 0,1172 ]   [ 16 queries used ]   [ Generated: 26.04.24, 21:13 GMT ]