Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.191.5.239] |
|
Сообщ.
#1
,
|
|
|
Люди умеющие работать с функциональными ЯП, объясните, как
Например класс содержащий какие-то там поля-свойства и методы. Как этот объект представляется в функциональных языках? Посмотрим на Erlang, педивикия пишет, что можно задействовать кортеж, но можно ли обратиться к элементам кортежа по именам полей? Как реализовать всякие методы? Делать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C? |
Сообщ.
#2
,
|
|
|
Цитата applegame @ объект с полями и указателями на методы, если совсем кратко Делать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C? |
Сообщ.
#3
,
|
|
|
Цитата SVK @ И как создать такой объект? Можно простейший пример на том же Erlang? объект с полями и указателями на методы, если совсем кратко |
Сообщ.
#4
,
|
|
|
Цитата applegame @ 1) Люди умеющие работать с функциональными ЯП, объясните, как 2) Например, класс содержащий какие-то там поля-свойства и методы. 3) Как этот объект представляется в функциональных языках? 4) Посмотрим на Erlang, педивикия пишет, что можно задействовать кортеж, но можно ли обратиться к элементам кортежа по именам полей? 5) Как реализовать всякие методы? Делать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C? 1) В общем случае никак, это же другой подход, без объектов. В частных случаях — с помощью замыканий, например. 2) Это не пример. Программирование существует не ради программирования, а объекты не ради объектов. 3) См. пункт 1. 4) У кортежа нет имён полей, не понимаю, о чём ты. 5) См. пункт 1. Все ответы на твои вопросы есть в SICP: Lecture 6A Lecture 6B |
Сообщ.
#5
,
|
|
|
Цитата SVK @ Цитата applegame @ объект с полями и указателями на методы, если совсем краткоДелать кучу глобальных функций принимающих в качестве первого аргумента объект, как в старом добром C? Ты функциональное программирование с процедурным не путаешь? Какие указатели в функциональном программировании? Добавлено applegame, может начать со scala/f#, просто оставить классы на какое-то время и посмотреть, к чему в итоге придёт проект? Добавлено Цитата applegame @ Например класс содержащий какие-то там поля-свойства и методы. Как этот объект представляется в функциональных языках? В функциональных языках лучше это никак не представлять, а спроектировать другое решение исходной задачи. |
Сообщ.
#6
,
|
|
|
Цитата korvin @ Это из педивикии, что-то я не так понял видимо:4) У кортежа нет имён полей, не понимаю, о чём ты. Цитата Wikipedia..Erlang.Кортеж Что такое кортеж, объяснять не нужно.В Erlang принято строить различные типы данных на основе кортежей с тегами, что облегчает отладку программы и считается хорошим стилем программировани Цитата korvin @ 1) В общем случае никак, это же другой подход, без объектов. В частных случаях — с помощью замыканий, например. Цитата D_KEY @ В функциональных языках лучше это никак не представлять, а спроектировать другое решение исходной задачи. Я об этом, по сути, и спрашиваю. Смотришь на всякие учебники для новичков, а там туфта вроде подсчета чисел Фибоначчи, сортировки списков, всяких map/reduce, которые есть в любом современном языке и нихрена функциональному подходу не учат. Поэтому и говорю о ломании шаблона. Я при проектировании задачи сразу начинаю мыслить объектами. Как от этого уйти? Допустим простейший игровой сервер, где есть карта, есть NPC бродящие по этой карте и игроки. Ну вот как это спроектировать без ООП, ума не приложу? Ну карта - двумерный массив, а мобы и игроки? Добавлено Цитата D_KEY @ Да, я тоже подумал что что-то недопонято. Я ковырял в свое время игровой сервер написанный целиком на C. Но там один хрен было ООП, как раз через поля структур в виде указателей. Там даже было наследование этих структур сымитировано через касты. Ты функциональное программирование с процедурным не путаешь? Какие указатели в функциональном программировании? |
Сообщ.
#7
,
|
|
|
D_KEY: да, попутал %)
|
Сообщ.
#8
,
|
|
|
Цитата korvin @ А на русском есть что-нибудь подобное. Я тяжело воспринимаю английский на слух. Все ответы на твои вопросы есть в SICP: Добавлено Вот нашел первое попавшееся - http://www.youtube.com/watch?v=Ze6qdlyz-30 буду посмотреть. |
Сообщ.
#9
,
|
|
|
Цитата applegame @ Цитата korvin @ А на русском есть что-нибудь подобное. Я тяжело воспринимаю английский на слух.Все ответы на твои вопросы есть в SICP: Ну книга SICP переведена на русский("структура и имплементация компьютерных программ"). Но я бы тебе ее рекомендовать не стал... Она слишком базовая. В MIT этот курс был для CS, а не SE. Так что на счёт использования для начала функциональных языков, которые умеют ООП? Ocaml, F#, Scala. А там постепенно где видишь пользу от функционального программирования, там и используешь. Мне вообще кажется, что эти подходы вполне себе существуют вместе, на более высоком уровне у нас ОО-система, а на более детальном уровне функциональщина("конвейерная" работа с потоками данных, как можно меньше мутабельности, простое распараллеливание, сопоставление с образцом, алгебраические типы данных и пр.). Добавлено Цитата applegame @ Поэтому и говорю о ломании шаблона. Я при проектировании задачи сразу начинаю мыслить объектами. Как от этого уйти? Допустим простейший игровой сервер, где есть карта, есть NPC бродящие по этой карте и игроки. Ну вот как это спроектировать без ООП, ума не приложу? На последнем докладе о функциональщине, что я видел(вживую), докладчик рассматривал пример игры "жизнь". А потом спел песню про монады Ссылку могу дать чуть позднее. Там на русском. Добавлено Agile Days 2015, Вагиф Абилов: А нам-то зачем функциональное программирование? Цитата В докладе на простых примерах дается представление о том, чем отличается моделирование предметной области и реализация функционала при использовании функциональных языков - таких, как F#, Scala и Clojure, в сравнении с объектно-ориентированными C# и Java. Какие типы задач наиболее подходят для функциональных языков? Как их лучше внедрять в проект? И как убедить руководство в практичности такого выборы? Обо всем этом пойдет речь в докладе. Ну рассказал он не обо всем. Зато спел |
Сообщ.
#10
,
|
|
|
Цитата applegame @ Это из педивикии, что-то я не так понял видимо: Цитата Wikipedia..Erlang.Кортеж Что такое кортеж, объяснять не нужно.В Erlang принято строить различные типы данных на основе кортежей с тегами, что облегчает отладку программы и считается хорошим стилем программировани А, кортеж с тэгами, это другое дело. По сути это записи (records) =) Это уже зависит от конкретного языка, в Хаскелле, например, получить значения полей можно с помощью одноимённых функций-селекторов, которые автоматически генерируются при объявлении типа-записи. Не очень удобное решение, получается невозможно определить в одном модуле два типа-записи с одноимённым полем, будет конфликт имён. В Ocaml записи получше сделаны, ИМХО. Какой синтаксис в Erlang'е для работы с такими типами — ХЗ. Ну и конечно сопоставление с образцом (pattern matching), если у тебя не opaque-тип. Цитата applegame @ Я об этом, по сути, и спрашиваю. Смотришь на всякие учебники для новичков, а там туфта вроде подсчета чисел Фибоначчи, сортировки списков, всяких map/reduce, которые есть в любом современном языке и нихрена функциональному подходу не учат. Почитай Real World Haskell или Real World Ocaml (правда в Ocaml есть и классы с объектами, и сайд-эффекты можно использовать свободно, без контроля со стороны системы типов, поэтому Хаскелл в целом будет более нагляден). Там более практичные примеры. Цитата applegame @ Поэтому и говорю о ломании шаблона. Я при проектировании задачи сразу начинаю мыслить объектами. Как от этого уйти? Допустим простейший игровой сервер, где есть карта, есть NPC бродящие по этой карте и игроки. Ну вот как это спроектировать без ООП, ума не приложу? Ну карта - двумерный массив, а мобы и игроки? Записи. |
Сообщ.
#11
,
|
|
|
Цитата D_KEY @ Спасибо за ссылку, интересный и достаточно веселый доклад, хотя нового оттуда я мало чего почерпнул, скорее слегка углубил, то что уже знал. Кроме монад конечно. Я еще ими серьезно не занимался, а из доклада так и не понял разницу между исключениями и монадами. Также не совсем понял, что он там говорил об описании неких доменов. Это надо отдельно читать.Так что на счёт использования для начала функциональных языков, которые умеют ООП? Ocaml, F#, Scala. А там постепенно где видишь пользу от функционального программирования, там и используешь. Мне вообще кажется, что эти подходы вполне себе существуют вместе, на более высоком уровне у нас ОО-система, а на более детальном уровне функциональщина("конвейерная" работа с потоками данных, как можно меньше мутабельности, простое распараллеливание, сопоставление с образцом, алгебраические типы данных и пр.). Agile Days 2015, Вагиф Абилов: А нам-то зачем функциональное программирование? Цитата В докладе на простых примерах дается представление о том, чем отличается моделирование предметной области и реализация функционала при использовании функциональных языков - таких, как F#, Scala и Clojure, в сравнении с объектно-ориентированными C# и Java. Какие типы задач наиболее подходят для функциональных языков? Как их лучше внедрять в проект? И как убедить руководство в практичности такого выборы? Обо всем этом пойдет речь в докладе. Ну рассказал он не обо всем. Зато спел В качестве небольшого упражнения переписал F# код для игры Жизнь из лекции Абилова на D (фактически портирование): Игра жизнь на D в функциональном стиле 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 пишут распределенные системы, веб-сервера и все такое. В веб-сервере запросы зачастую не могут быть чистыми функциями, например запись в БД - вполне себе сайд-эффект. Что-то мне подсказывает что тут-то и начинается расколбас с монадами. Это так? Добавлено Еще вот интересный момент: у Абилова в примере с игрой Жизнь, в функции создания списка соседей присутствуют циклы. Разве в труЪ функциональном подходе циклы в традиционном понимании используются? |
Сообщ.
#12
,
|
|
|
Цитата 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, который обычно считается очень ООП-шной предметной областью. |
Сообщ.
#13
,
|
|
|
Цитата 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 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; } |
Сообщ.
#14
,
|
|
|
Смотри как бы у тебя не развился D головного мозга Все зачем-то на D переводишь. Это скорее вредно при попытках разобраться в чем-либо.
|
Сообщ.
#15
,
|
|
|
Цитата applegame @ 1) Да, но ведь такая функция получается не "чистая". 2) Ну хорошо. Прочитал вот тут про монаду состояния. Автор сэмулировал ее в жабаскрипте. Меня удивило, что ничего особенно сложного (по крайней мере в этом типе монад) я не заметил. Возможно я что-то тотально не понял и мне только показалось, что все понятно. И вот я сделал аналогичный код на D для того же стека на базе массива. Можно ли считать, что я сделал аналог монады состояния или все же, что-то было глобально не понято? Функции push и pop каррированы вручную, хотя в D для это есть бибилиотечная функция. http://dpaste.dzfl.pl/bcf704330224 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 определяется автоматически или что-то в таком духе). Т.е. на «традиционном ООП языке» Монада выглядит так: // 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, именно она позволяет компилятору выстроить правильную последовательность действий в программе. |
Сообщ.
#16
,
|
|
|
Небольшое уточнение: «монада как бы "заразна"» — это касается монады IO (и, вероятно, некоторых других), т.к. Монада — это класс типа, а не сам тип, она не регулирует конструирование значений типа и не запрещает наличие функций, вытаскивающих значение из монадного типа, т.е. невозможность «вытащить» значение из монады произвольным способом обеспечивается модулем, в котором тип определён. Например модуль, в котором определён тип ST, предоставляет функцию runST, вытаскивающую значение из монады ST, т.к. ST — чистый тип, без сайд-эффектов. Монаду можно определить даже для списков, чтобы воспользоваться do-нотацией:
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 → [(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 |
Сообщ.
#17
,
|
|
|
Цитата D_KEY @ Уже развился до терминальной стадии. Но это не ослепляет меня. В D полно говна, но пока это лучшее, из того, что у меня есть.Смотри как бы у тебя не развился D головного мозга Цитата D_KEY @ На D потому что в данный момент это язык, которым я владею намного лучше остальных языков. Я мог бы ваять примеры на Ruby или плюсах, но это заняло бы гораздо больше времени.Все зачем-то на D переводишь. Это скорее вредно при попытках разобраться в чем-либо. Цитата korvin @ Совершенно, абсолютно бесполезное занятие, особенно для объяснения, что такое монада. Автор вообще не понял, что это, смешал в кучу монаду, ST и ленивость. Зачем? Монада IO в JS, да и в (почти) любом императивном языке — это точка с запятой ( ; ), точнее это функция bind, именно она позволяет компилятору выстроить правильную последовательность действий в программе. Насчет точки с запятой - очень хорошая аналогия. помогла мне кое-что осознать. С точки зрения функционального программирования не существует алгоритма, как некоей последовательности действий. Это, ИМХО, крайне важный момент, которому, я считаю, уделяют слишком мало внимания в лекциях и учебниках. Сама природа человека строит все на цепочках событий, а тут надо забыть про это. Есть исходный материал, а есть множество функций от этого материала и множество результатов этих функций. Причем и материалы и результаты также могут быть функциямии. Все это происходит вне времени. Время конечно может участвовать как агрумент функций, но сами функции "плавают" в некоем безвременном "гиперпространстве". Суть монады, насколько я понял, как раз в формализации последовательности из двух вычислений. При этом значения аргумента и результата обернуты в монаду, а результат первого вычисления является аргументом второго вычисления. Своим "примером" монады, я скорее сымитировал один из вариантов реализации внутренностей монады, а не саму монаду. |
Сообщ.
#18
,
|
|
|
Цитата applegame @ Насчет точки с запятой - очень хорошая аналогия. помогла мне кое-что осознать. С точки зрения функционального программирования не существует алгоритма, как некоей последовательности действий. Это, ИМХО, крайне важный момент, которому, я считаю, уделяют слишком мало внимания в лекциях и учебниках. Сама природа человека строит все на цепочках событий, а тут надо забыть про это. Есть исходный материал, а есть множество функций от этого материала и множество результатов этих функций. Причем и материалы и результаты также могут быть функциямии. Все это происходит вне времени. Время конечно может участвовать как агрумент функций, но сами функции "плавают" в некоем безвременном "гиперпространстве". Суть монады, насколько я понял, как раз в формализации последовательности из двух вычислений. При этом значения аргумента и результата обернуты в монаду, а результат первого вычисления является аргументом второго вычисления. Своим "примером" монады, я скорее сымитировал один из вариантов реализации внутренностей монады, а не саму монаду. Это всё пустые философствования. Ты не можешь вернуться в прошлое, например, таким образом пространство-время — это просто поток, ленивый список моментов, вычисляющийся по мере необходимости (течении времени), преобразование одного момента в другой — чистая функция. Но что толку от всех этих аналогий? А суть монады ты неправильно понял, как и тот автор, я ж написал пример на предыдущей странице. У монады нет никаких внутренностей, это интерфейс. |
Сообщ.
#19
,
|
|
|
Цитата korvin @ Философствования - да. Пустые ли они? Не уверен. Для проектирования задачи важно понимание. Перестать мыслить в рамках последовательности действий и начать мыслить в рамках функциональных трансформаций. Фиг его знает, мне помогает.Это всё пустые философствования. Ты не можешь вернуться в прошлое, например, таким образом пространство-время — это просто поток, ленивый список моментов, вычисляющийся по мере необходимости (течении времени), преобразование одного момента в другой — чистая функция. Но что толку от всех этих аналогий? Цитата korvin @ Странно. У абстрактной монады, конечно, нет никаких внутренностей, но как же конкретные реализации вроде монады Maybe? Разве у нее нет внутренностей? Даже в твоем примере у класса IO<A> вполне есть "внутренности": join и bind. А суть монады ты неправильно понял, как и тот автор, я ж написал пример на предыдущей странице. У монады нет никаких внутренностей, это интерфейс. |
Сообщ.
#20
,
|
|
|
Цитата applegame @ Странно. У абстрактной монады, конечно, нет никаких внутренностей, но как же конкретные реализации вроде монады Maybe? Разве у нее нет внутренностей? Даже в твоем примере у класса IO<A> вполне есть "внутренности": join и bind. Странно, у интерфейса Monad, конечно, нет никаких внутренностей, но как же конкретные реализации вроде класса IO? Разве у класса нет внутренностей? Я не понимаю, в чём твоя проблема, в этом вашем D нет интерфейсов и классов? |
Сообщ.
#21
,
|
|
|
Цитата korvin @ Проблем никаких нет. Ответ ты и сам знаешь. Я не понимаю, в чём твоя проблема, в этом вашем D нет интерфейсов и классов? |
Сообщ.
#22
,
|
|
|
Попрбовал Haskell и OCaml.
Первый кошерен, второй мерзок. Кому пришла в голову бредовая идея делать отдельные математические операторы для int и float? Разделение элементов списка точками с запятой? Фу короче. А вот Хаскель - приятная штука. |
Сообщ.
#23
,
|
|
|
В хаскеле проблема ad-hoc полиморфизма просто решена здраво через type class'ы. В ocaml так просто не сделаешь, потому и отдельные операции там, где это важно с точки зрения типизации. Вместо ocaml можешь попробовать F#, это почти тот же язык, только причесанный и с многими дополнительными плюшками.
|
Сообщ.
#24
,
|
|
|
Цитата D_KEY @ Он неплох, я уже немного видел его в приведеном тобой ролике с песней о монадах. Если бы не его привязанность к .NET, может быть и поковырялся бы с ним.Вместо ocaml можешь попробовать F#, это почти тот же язык, только причесанный и с многими дополнительными плюшками. Пока поизучаю Haskell, тем более что многие вещи оказались знакомы по D (D_LANG гойловного мозга налицо). Например, бесконечные ленивые списки == бесконечные ленивые ranges. |
Сообщ.
#25
,
|
|
|
Цитата 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'шных рейнджах выглядеть такой код: fibs = 0 : 1 : zipWith (+) fibs (tail fibs) main = print $ take 10 fibs [0,1,1,2,3,5,8,13,21,34] В Хаскелле ленивы не только списки, он ленив весь. |
Сообщ.
#26
,
|
|
|
Цитата korvin @ Или то, что ты не можешь просто взять и вставить в середину функции отладочную печать, т.к. IO заразна и тебе придётся менять тип своей чистой функции на (IO T) (а то и переписывать её полностью). А разве там нет специального костыля с unsafePerformIO как раз для дебажного принта? Цитата korvin @ Гм… А как будет на D'шных рейнджах выглядеть такой код: Ну в расте fibs будет возвращать итератор. Подозреваю, что рейнжи в D работают точно так же. |
Сообщ.
#27
,
|
|
|
Цитата korvin @ Гм… А как будет на D'шных рейнджах выглядеть такой код: fibs = 0 : 1 : zipWith (+) fibs (tail fibs) main = print $ take 10 fibs [0,1,1,2,3,5,8,13,21,34] Аналогичный бесконечный ленивый рендж для чисел Фибоначчи можно сделать вот так: 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)); } (a,n) => a[n-1] + a[n-2] // лямбда вычисляющая очередное число (0, 1) // первые два числа Фибоначчи Результат - http://dpaste.dzfl.pl/9850c213f69a Цитата korvin @ В Хаскелле ленивы не только списки, он ленив весь. D по умолчанию не ленив, но ленивость можно указать явно: 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()); } flag = true foo data = 5 flag = false |
Сообщ.
#28
,
|
|
|
Цитата DarkEld3r @ 1) А разве там нет специального костыля с unsafePerformIO как раз для дебажного принта? 2) Ну в расте fibs будет возвращать итератор. Подозреваю, что рейнжи в D работают точно так же. 1) Есть такое выражение: «если вы используете UnsafePreformIO, то вам не нужен Haskell» или как-то так. 2) Я жду код, а не рассуждения. Добавлено Цитата applegame @ D по умолчанию не ленив, но ленивость можно указать явно: 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()); } flag = true foo data = 5 flag = false Тут-то D и сел в лужу. Ибо сочетать ленивость и сайд-эффекты — это не в тапки гадить. http://ideone.com/A0pXvm 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 → flag = True foo data = 5 flag = False foo |
Сообщ.
#29
,
|
|
|
korvin, раскрой мысль. Не очень понял, почему второй раз правильно вызывать foo. В чем фейл D?
Добавлено И вообще, если ты сделаешь x <- d внутри if'а, то будет так же, как в D |
Сообщ.
#30
,
|
|
|
Цитата korvin @ Твой код не эквивалентен моему.Тут-то D и сел в лужу. Ибо сочетать ленивость и сайд-эффекты — это не в тапки гадить. Цитата D_KEY @ Ага, korvin смухлевал. И вообще, если ты сделаешь x <- d внутри if'а, то будет так же, как в D Вот как должно было быть: http://ideone.com/GO7ZhH 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 flag = True foo data = 5 flag = False |
Сообщ.
#31
,
|
|
|
Цитата applegame @ Аналогичный бесконечный ленивый рендж для чисел Фибоначчи можно сделать вот так: D не знаю, так что интересно - take возвращает список или новый рейндж? Цитата korvin @ 1) Есть такое выражение: «если вы используете UnsafePreformIO, то вам не нужен Haskell» или как-то так. Какая-то странная категоричность. Напрямую UnsafePreformIO использовать не надо, надо использовать Debug.Trace, который, вроде, из коробки есть. Ну и хочешь сказать это решение хуже, чем уродовать функции ради отладки? Цитата korvin @ 2) Я жду код, а не рассуждения. Ну код уже есть и похоже D работает не так как я предположил. Мой вариант просто и хоть на плюсах изобразить можно. Хотя, конечно, это будет не прямой аналог. Смысл в следующем: fibs в расте вернёт итератор, который хранит в себе состояние и на котором можно бесконечно звать next, ну а take ограничит этот итератор. Не думаю, что код что-то изменит. |
Сообщ.
#32
,
|
|
|
Цитата DarkEld3r @ Список? Ты наверное имел в виду массив. Нет не список/масив, а новый рейндж. Тоже ленивый. Функции обработки рейнджей в D сделаны лениво везде, где можно сделать их лениво. В частности map и filter тоже возвращают ленивые рейнджи.D не знаю, так что интересно - take возвращает список или новый рейндж? Цитата DarkEld3r @ На самом деле в D все точно так же. recurrence вовзращает рейндж хранящий внутри состояние. Рейнджи в D не являются встроенными в язык - это библиотечные сущности. Это, конечно, работает не так как корвиновский список складывающийся с самим собой со смещением, но результат аналогичен. Ну код уже есть и похоже D работает не так как я предположил. Мой вариант просто и хоть на плюсах изобразить можно. Хотя, конечно, это будет не прямой аналог. Смысл в следующем: fibs в расте вернёт итератор, который хранит в себе состояние и на котором можно бесконечно звать next, ну а take ограничит этот итератор. |
Сообщ.
#33
,
|
|
|
Цитата applegame @ Это не принципиально. Список? Ты наверное имел в виду массив. Меня как раз интересовало как раз "контейнер" там или "честный рейндж". Цитата applegame @ Понятно. Ну в расте, это всё-таки больше "итераторы". В смысле, вывести на печать (стандартными средствами) итератор нельзя. То есть между print и take надо или собирать значения в контейнер или организовывать цикл для прохода. На самом деле в D все точно так же. recurrence вовзращает рейндж хранящий внутри состояние. |
Сообщ.
#34
,
|
|
|
Цитата DarkEld3r @ В смысле, вывести на печать (стандартными средствами) итератор нельзя. То есть между print и take надо или собирать значения в контейнер или организовывать цикл для прохода. Странный он, этот ваш rust |
Сообщ.
#35
,
|
|
|
Цитата DarkEld3r @ Дешный writeln/writefln почти всеяден. Фактически типобезопасный printf.Понятно. Ну в расте, это всё-таки больше "итераторы". В смысле, вывести на печать (стандартными средствами) итератор нельзя. То есть между print и take надо или собирать значения в контейнер или организовывать цикл для прохода. Ну вот, шёл, понимаешь, кругом красивая природа, солнышко и тут бац - вступил в говно foldl vs foldl' Если вдруг у вас переполнение стека, то вместо ленивого foldl воспользуйтесь более strict(строгим?) foldl'. WTF? На самом деле я, конечно, все понимаю: рекурсия, ленивость и все такое, но математическую красоту слегка опоносили. Это наверное как раз об этом: Цитата korvin @ Да, но мне интересно, что ты скажешь, когда познакомишься с ним получше и узнаешь, что, например, написав хвостово-рекурсивную функцию внезапно обнаружишь переполнение стека, из-за ленивости, и окажется, что нужно форсировать вычисления специальным образом. |
Сообщ.
#36
,
|
|
|
Цитата D_KEY @ Странный он, этот ваш rust Почему странный? В С++ ведь тоже нельзя просто так в printf/cout пару итераторов передать. Написать функцию которая "проходится по итератору" в расте тоже проблем никаких нет. Аналога ostream_iterator из коробки и правда нет. Но тут как раз всё дело в том, что применение map, filter и других подобных функций возвращает новый ленивый итератор. Для того чтобы получить значения надо итерироваться, ну или собрать значения куда-то. Добавлено Цитата applegame @ Дешный writeln/writefln почти всеяден. Фактически типобезопасный printf. В расте print! - макрос, так что тоже может много всего, но не работать с итераторами. Подозреваю, что это сознательное решение - мол мало ли что может скрываться за итератором и т.д. К тому же, рейнжи в расте не копируемые. |
Сообщ.
#37
,
|
|
|
Цитата applegame @ Ну вот, шёл, понимаешь, кругом красивая природа, солнышко и тут бац - вступил в говно foldl vs foldl' Если вдруг у вас переполнение стека, то вместо ленивого foldl воспользуйтесь более strict(строгим?) foldl'. WTF? На самом деле я, конечно, все понимаю: рекурсия, ленивость и все такое, но математическую красоту слегка опоносили. Это наверное как раз об этом: Цитата korvin @ Да, но мне интересно, что ты скажешь, когда познакомишься с ним получше и узнаешь, что, например, написав хвостово-рекурсивную функцию внезапно обнаружишь переполнение стека, из-за ленивости, и окажется, что нужно форсировать вычисления специальным образом. Нет, не об этом. Как раз наоборот, ленивые языки более «математически красивы» (что бы ты под этим не подразумевал), «уродствами» они становятся с другой, технической точки зрения. Добавлено Цитата applegame @ Ага, korvin смухлевал. Не, просто поспешил. Добавлено Цитата DarkEld3r @ Какая-то странная категоричность. Напрямую UnsafePreformIO использовать не надо, надо использовать Debug.Trace, который, вроде, из коробки есть. Ну и хочешь сказать это решение хуже, чем уродовать функции ради отладки? Трассировка не всегда именно то, что нужно. А сказать я хочу, что ленивость-по-умолчанию не всегда удобна. |
Сообщ.
#38
,
|
|
|
Цитата korvin @ Какое-то непонимание. Полагаю, что мы говорили об одном и том же. Под "опоносили" я подразумевал не ленивость, а как раз необходимость всяких обходных маневров вроде специальных версий тех же самых функций. Я понимаю, что это вынужденная мера, но осадочек-то остается. Нет, не об этом. Как раз наоборот, ленивые языки более «математически красивы» (что бы ты под этим не подразумевал), «уродствами» они становятся с другой, технической точки зрения. |
Сообщ.
#39
,
|
|
|
Цитата applegame @ Какое-то непонимание. Полагаю, что мы говорили об одном и том же. Под "опоносили" я подразумевал не ленивость, а как раз необходимость всяких обходных маневров вроде специальных версий тех же самых функций. Я понимаю, что это вынужденная мера, но осадочек-то остается. А, ну да, я это имел в виду. =) |