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

    ExpandedWrap disabled
      -- Функция, которая возвращает действие ввода/вывода,
      -- в котором завернут Nothing, явно типизированный как Maybe Int
      fun1 :: IO (Maybe Int)
      fun1 = return (Nothing)
       
      -- функция проверки
      testIO = do
          -- здесь будет вызван fail, т.к. ждем Just, а приходит Nothing:
          (Just x) <- fun1
          print x


    В монаде IO fail кидает ошибку. Проверяем:
    ExpandedWrap disabled
      main = testIO

    ExpandedWrap disabled
      main: user error (Pattern match failure in do expression...


    Теперь возьмем другую монаду, где fail означает не ошибку, а какое-то значение. Тут подойдет список:
    ExpandedWrap disabled
      -- делаем тоже самое:
       
      fun2 :: [Maybe Int]
      fun2 = return (Nothing) -- тоже что [Nothing]
       
      -- Проверяем список
      testList = do
          -- здесь будет вызван fail, т.к. ждем Just, а приходит Nothing:
          (Just x) <- fun2
          return (x)


    Для списка fail просто возвращает пустой список. Проверяем:
    ExpandedWrap disabled
      main = print testList

    ExpandedWrap disabled
      []


    Добавлено
    Если что, Maybe - это тип, который имеет два конструктора:
    ExpandedWrap disabled
      data Maybe a = Just a | Nothing


    Соответственно, в наших тестах мы выполняем сопоставление с образцом и ждем конструктор Just, а приходит туда Nothing. В случае монадического вычисления с IO мы получаем ошибку, а для монадического вычисления со списками - пустой список. Поскольку в C++ нет сопоставления с образцом, пользы от fail нет никакой. А понимание монад он никак не проясняет(скорее мешает).
    Сообщение отредактировано: D_KEY -
      Цитата D_KEY @
      Поскольку в C++ нет сопоставления с образцом, пользы от fail нет никакой.

      А может ли служить в Цэ++ в качестве "сопоставления с образцом" - std::type_info (в частности std::type_info::before)?
      Если конечно я правильно начинаю осознавать реальность :lol:
      В случае наследования с использованием RTTI, мы сможем вырвать кусок у объекта на произвольном уровне иерархии (с помощью его конструктора). А вот если нужный нам конструктор отсутствует в иерархии - тогда и нужно выкидывать fail. Кстати, а как его в Цэ++ реализовать, исключение бросить штоле?

      Извини, что достаю вопросами. Просто пока не могу распознать ценности всего этого для меня.

      Попутно еще одни вопрос. Многие в процессе моделирования используют/манипулируют понятиями Шаблоны Проектирования (ну или Паттерны). И это безотностительно к какому-то конкретному ЯП. Рассматриваемые "монады" какой(или какие совокупности) из паттернов реализуют? И вообще можно ли так говорить относительно монад?
        Да любой код на C++ уже считай монада. Операции идут одна за другой - чем не монада?
          Цитата applegame @
          Да любой код на C++ уже считай монада. Операции идут одна за другой - чем не монада?

          Ну да, в принципе, монада. Почти IO, только захардкоженная.
          Другое дело, что писать свои монады в таком же виде нельзя.
          Да и смысла считать это монадой особого нет. C++ изначально ничего не говорит о чистоте.
            Списки тоже монады.
            Потому можно написать:
            ExpandedWrap disabled
              fun = do
                  x <- [1, 2, 3]
                  y <- [4, 5, 6]
                  return (x, y)
               
              main = print fun

            И получить:
            ExpandedWrap disabled
              [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]


            На самом деле тут использована do-нотация, которая представляет из себя синтаксический сахар. Рассахаривается это в
            ExpandedWrap disabled
              fun =
                  [1, 2, 3] >>= \x ->
                  [4, 5, 6] >>= \y ->
                  return (x, y)


            Т.е. так:
            ExpandedWrap disabled
              fun =
                  [1, 2, 3] >>= (\x ->
                      [4, 5, 6] >>= (\y ->
                          return (x, y)))


            Что на крестах выглядит примерно так:
            ExpandedWrap disabled
              auto res = mk_list({1, 2, 3}) >>= [](auto x) {
                  return mk_list({4, 5, 6}) >>= [=](auto y) {
                      return ret<list>(std::make_pair(x, y));
                  };
              };


            С тем же результатом:
            ExpandedWrap disabled
              [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]


            Для этого нужно сделать списки и реализовать для них монаду.
            список(на основе вектора)
            ExpandedWrap disabled
              template<typename T>
              struct list : private std::vector<T> {
                  using impl = std::vector<T>;
                  using impl::value_type;
               
                  list() = default;
                  list(std::initializer_list<T> il)
                      : std::vector<T>(std::move(il))
                  {
                  }
               
                  using impl::push_back;
                  using impl::insert;
                  using impl::begin;
                  using impl::end;
              };
               
              auto mk_list(std::initializer_list<T> il) -> list<T>
              {
                  return list<T>(il);
              }


            Реализуем монаду для списка:
            ExpandedWrap disabled
              template<>
              template<typename T>
              auto Monad<list>::ret(T a) -> list<T>
              {
                  // заворачиваем объект в список
                  return list<T>{a};
              }
               
              template<>
              template<typename T, typename U>
              auto Monad<list>::bind(list<T> ma, std::function<list<U>(T)> f) -> list<U>
              {
                  // формируем список списков путем применения к каждому элементу переданной функции
                  // (которая принимает объект, а возвращает монаду(список))
                  list<list<U> > tmp;
                  std::transform(ma.begin(), ma.end(), std::back_inserter(tmp), f);
               
                  // конкатенируем списки в один
                  list<U> res;
                  for (auto & v : tmp) {
                      res.insert(res.end(), v.begin(), v.end());
                  }
                  return res;
              }


            Ну и для вывода пара вспомогательных функций:
            вывод пар и списков
            ExpandedWrap disabled
              template<typename T, typename U>
              std::ostream & operator << (std::ostream & os, std::pair<T,U> p)
              {
                  os << "(" << p.first << "," << p.second << ")";
                  return os;
              }
               
              template<typename T>
              std::ostream & operator << (std::ostream & os, const list<T> & a)
              {
                  os << "[";
                  int i = 0;
                  for (auto & e : a) {
                      if (i++) os << ",";
                      os << e;
                  }
                  os << "]";
                  return os;
              }


            http://ideone.com/bJjEW3
            Сообщение отредактировано: D_KEY -
              Теперь по поводу IO.

              Смысл IO в добавлении работы с побочными эффектами в чистый язык. Одним из способов(в каком-то смысле теоретическим) представления побочных эффектов в чистом языке является работа с некоторым объектом, представляющим "реальный мир".

              И каждое действие с побочными эффектами в чистом языке мы будем представлять в виде функций вида (real_world) -> (real_world, data). Т.е. мы как будто не меняем внешние условия, а возвращаем новое состояние мира вместе с каким-то значением.
              IO<T> как раз представляет собой обертку над (real_world) -> (real_world, T) и позволяет через монадический интерфейс работать с вычислениями, "меняющими мир"(через якобы возврат "нового мира").

              В Haskell тип RealWorld скрыт от пользователя, как и внутренняя структура IO, т.е. наружу торчит только тип IO a, но запаковываем значение мы через return, а достаем значение только внутри монадического вычисления(как и в других монадах - через лямбду или <- в do-нотации).

              Теперь возвращаемся к C++.
              ExpandedWrap disabled
                struct real_world
                {
                    explicit real_world(size_t i = 0) : m_i(i) {}
                 
                    real_world(const real_world &) = delete;
                    real_world(real_world && other) : m_i(other.m_i) {}
                 
                    const real_world & operator = (const real_world & other) = delete;
                    const real_world & operator = (real_world && other) = delete;
                 
                    // "меняем мир"
                    // принимаем функцию с побочными эффектами,
                    // возвращаем пару из "нового мира" и результата функции
                    template<typename T, typename F>
                    std::pair<real_world, T> change(F f) const
                    {
                        return std::make_pair(real_world{m_i + 1}, f());
                    }
                 
                private:
                    const size_t m_i;
                };


              IO:
              ExpandedWrap disabled
                template<typename T>
                struct IO {
                   // инкапсулируем функцию, маскирующую побочные эффекты через возврат "нового мира"
                    using f_type = std::function<std::pair<real_world, T>(real_world)>;
                 
                    explicit IO(f_type f)
                        : m_f(std::move(f))
                    {
                    }
                 
                    // запускаем функцию, которая порождает "новый мир" и значение
                    auto run(real_world w) const -> std::pair<real_world, T>
                    {
                        return m_f(std::move(w));
                    }
                 
                private:
                    f_type m_f;
                };


              Реализуем монаду:
              ExpandedWrap disabled
                template<>
                template<typename T>
                auto Monad<IO>::ret(T a) -> IO<T>
                {
                    // создаем IO с функцией, которая просто создает пару из "мира" и значения,
                    // не производя побочных вычислений и не меняя мир
                    return IO<T>([=](real_world w){return std::make_pair(std::move(w), a);});
                }
                 
                template<>
                template<typename T, typename U>
                auto Monad<IO>::bind(IO<T> ma, std::function<IO<U>(T)> f) -> IO<U>
                {
                    // возвращаем IO с функцией, которая принимает "мир",
                    // запускает на нем первое вычисление, получает "следующий мир" и новое значение,
                    // затем вычисляем функцию от значения, получая новое IO вычисление,
                    // которое и запускаем над "следующим миром"
                    return IO<U>([=](real_world w) {
                        auto world_and_data = ma.run(std::move(w));
                        auto next_io = f(world_and_data.second);
                        return next_io.run(std::move(world_and_data.first));
                    });
                }


              Теперь определим простейшие функции с IO, работающие через реальные функции с побочными эффектами
              ExpandedWrap disabled
                // заводим тип для "ничего", чтобы не заморачиваться с обработкой void
                struct unit {};
                 
                auto putChar(char ch) -> IO<unit>
                {
                    // Создаем IO с функцией, которая принимает "мир"
                    // и выводит символ на экран, возвращая новый "мир"
                    // (см. real_world::change)
                    return IO<unit>([=](real_world w) {
                        return w.change<unit>([=]{
                            std::cout << ch;
                            return unit{};
                        });
                    });
                };
                 
                auto getChar(unit) -> IO<char>
                {
                    // Создаем IO с функцией, которая принимает "мир"
                    // и читает символ, возвращая новый "мир" и прочитанный символ
                    // (см. real_world::change)
                    return IO<char>([=](real_world w) {
                        return w.change<char>([=]{
                            char res;
                            if (!std::cin.get(res).good()) return '\0'; // пока так
                            return res;
                        });
                    });
                }


              Теперь можно уже что-то сделать просто через IO и эти две функции.
              Для начала реализуем вывод строк через putChar.
              ExpandedWrap disabled
                auto putLine(std::string str) -> IO<unit>
                {
                    // если строка пустая, то возвращаем действие ввода-вывода,
                    // которое выводит символ '\n',
                    // и останавливаем рекурсию
                    if (str.empty()) {
                        return putChar('\n');
                    }
                    // возвращаем действие ввода-вывода, которое сначала выводит первый символ строки,
                    // затем рекурсивно выводит остаток строки
                    return putChar(str[0]) >> putLine(str.substr(1));
                }


              Теперь функцию считывания строки:
              ExpandedWrap disabled
                auto getLine(unit) -> IO<std::string>
                {
                    // возвращаем действие ввода-вывода, которое считывает первый символ,
                    // и если он означает конец строки,
                    // то возвращает действие ввода-вывода, которое вернет пустую строку,
                    // в противном же случае считываем(рекурсивно) строку и возвращаем действие ввода-вывода,
                    // которое вернет сконкатенированную строку из принятого ранее символа и новой строки
                    return getChar(unit{}) >>= [=](char ch) {
                        if (ch == '\n' || ch == '\r' || ch == '\0') {
                            return ret<IO>(std::string());
                        }
                        return getLine(unit{}) >>= [=](std::string str) {
                            return ret<IO>(ch + str);
                        };
                    };
                }


              Ну и проверяем.
              Вот такой простой код в do-нотации:
              ExpandedWrap disabled
                main = do
                    putLine "What is your name?"
                    name <- getLine
                    putLine ("Nice to meet you, " ++ name ++ "!")

              будет выглядеть вот так:
              ExpandedWrap disabled
                int main()
                {
                    // формируем вычисление ввода-вывода
                    auto io_main =
                        putLine("What is your name?") >>
                        getLine(unit{})               >>= [=](std::string name) {
                            return putLine("Nice to meet you, " + name + "!");
                        };
                 
                    // запускаем вычисление
                    io_main.run(real_world{});
                }


              http://ideone.com/WYK5GA
              Сообщение отредактировано: D_KEY -
                ХЗ зачем я все это делал, но было интересно :D
                  D_KEY
                  Да уж, продемонстрировали недюжинные познания. Жаль что во флейме, а так бы получили бы от меня +1 в тематике.
                  Придется это делать во флейме :D
                    Цитата D_KEY @
                    будет выглядеть вот так:

                    :crazy:
                      Цитата shm @
                      Цитата D_KEY @
                      будет выглядеть вот так:

                      :crazy:

                      Ну do-нотация это просто сахар. На самом деле компилятор haskell сначала превратит
                      ExpandedWrap disabled
                        main = do
                            putStrLn "What is your name?"
                            name <- getLine
                            putStrLn ("Nice to meet you, " ++ name ++ "!")

                      в
                      ExpandedWrap disabled
                        main =
                            putStrLn "What is your name?" >>
                            getLine                       >>= \name ->
                                putStrLn ("Nice to meet you, " ++ name ++ "!")

                      а потом уже будет с этим работать. Тут разница с кодом
                      ExpandedWrap disabled
                        auto io_main =
                            putLine("What is your name?") >>
                            getLine(unit{})               >>= [=](auto name) {
                                return putLine("Nice to meet you, " + name + "!");
                            };

                      Не такая уж и большая, да? :)

                      do-нотацию для С++ придумать сложнее... Сишные "макросы" тут слабоваты, а шаблоны синтаксис объявлений не поменяют.

                      Добавлено
                      Но вообще это бесполезная для С++ штука. Монада для списков и то полезнее.
                      Сообщение отредактировано: D_KEY -
                        Цитата D_KEY @
                        Не такая уж и большая, да?

                        Не сильна большая, но мозг выносит сильнее.
                          Вот тут как раз в тему :)
                          А вот тут даже я кое что понял. :yes:
                          Но вот этого я не могу понять.
                          Да, ведь можно нагородить операторов типа например "<>+->-" совершенно бессмысленных с точки зрения общепринятого понимания их составляющих - и потом гордо объявить, что это - операция "приковывать цепью"(с) хрен знает что к хрен знает чему.
                          Понятно, что любой язык программирования есть чистейшей воды формальная система.
                          Но должны же быть и некие пределы. :(
                          С этой точки зрения всем знакомый нам знак интеграла есть пример очевидной формализации целого алгоритма!!! - но совершенно осмысленный и понятный.
                          Именно это обстоятельство меня и настораживает. Слишком субъективны термины, понятия, операции и прочие процессы.
                          А это описание вообще шедевр:
                          Цитата
                          В функциональном программировании монада - структура, которая представляет вычисления, определенные как последовательности шагов: тип со структурой монады определяет то, что это означает приковывать цепью операции или функции гнезда того типа вместе. Это позволяет программисту строить трубопроводы, которые обрабатывают данные в шагах, в которых каждое действие украшено дополнительными правилами обработки, предоставленными монадой.
                          :'(
                          После трубопровода надо вводить категории вентиля, заглушки, датчиков давления и прочая...потом перейдем к канализации - в смысле объединения трубопроводов как каналов потоков информации...задачи фильтрации потоков канализации и тд... :wacko:
                          PS
                          В потоковом программировании тоже есть интересные нововведения, как например "цвет данных"
                          Какой то философ сказал
                          "ни одна вещь не должна объясняться сложнее чем она есть на самом деле"
                          Сообщение отредактировано: Oleg2004 -
                            О, там как раз для future реализация, то, что MyNameIsIgor говорил.

                            А вот do-нотацию я что-то не увидел...
                              Цитата JoeUser @
                              Читал доку по Эрлангу, видел там вычисление функции как последовательность утверждений, де жа вю ... Пролог.

                              Э-м... Можешь слегка по-подробней? А то у меня возникает стойкое ощущение, что тебя слегка ввела в заблуждение Прологоподобная грамматика Эрланга.

                              Добавлено
                              Цитата D_KEY @
                              Но вообще это бесполезная для С++ штука. Монада для списков и то полезнее.

                              А вот чистому Си не повредили бы, чтобы проверки кодов ошибок синтаксически лучше выглядели б. В Rust, я так понимаю, это сделали через макрос try! ?

                              Добавлено
                              Цитата D_KEY @
                              do-нотацию для С++ придумать сложнее... Сишные "макросы" тут слабоваты, а шаблоны синтаксис объявлений не поменяют.

                              А оператор ; переопределить нельзя? Я вроде уже спрашивал этот вопрос, может, не у тебя, но на этом форуме.
                              Сообщение отредактировано: korvin -
                                Цитата korvin @
                                А оператор ; переопределить нельзя? Я вроде уже спрашивал этот вопрос, может, не у тебя, но на этом форуме.

                                Нет, но operator , можно :)
                                Только я тут больше не о простой цепочке, а о <-. Т.е. даже если бы мы перегрузили ; , это бы не избавило нас от лямбд.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0753 ]   [ 16 queries used ]   [ Generated: 28.03.24, 23:10 GMT ]