Здравствуйте, alex_public, Вы писали: _>Так на диаграмме это просто отображается как некий префикс к имени класса. Типа std::string. Т.е. не получится строить из этого нормальную диаграмму (где есть всякие отношения и т.п.).
Так почему не получится? Есть у вас вложенные классы. Вы можете их обозначать просто с префикосом к имени, если этого достаточно. Можете с помощью имеющихся отношений (вы же сами ссылку на пример и дали) их обозначать, если префикса недостаточно. И этих возможностей хватит чтоб строить диаграммы для хаскель-кода. _>Напомню мою основную претензию, на которую так и не было ответа: как у нас отображаются функции (которые в отличие от ООП могут нести основную смысловую нагрузку программы) на диаграмме? У вас была попытка ответить через размещение модулей на диаграмме классов, но как мы видим это получается бредово.
Так в чем бредовость? Чем это бредовее отображения на диаграмме класса у которого есть методы и вложенные классы? _>Ага, какие-то попытки идти по правильному пути есть, но им ещё очень далеко до чего-то реального. И скорее всего так и будет оставаться, пока не будут разработаны и общеприняты (возможно включены в тот же UML) какие-то новые виды диаграмм, специально для функционального программирования.
Мы так и не выяснили, что туда нужно добавлять, если там и так уже все необходимое есть.
Впрочем, "так и будет оставаться" — это верно, потому что популярность такого рода диаграмм в ФП-комьюнити крайне низкая и почти никто в этом направлении не работает. K>>Массив значений алгебраических типов данных, конструкторы которых можно экспортировать, а можно — не. _>Воот, это уже ближе к делу... И как мы видим, уже никаких модулей
Как никаких модулей? Модули вот здесь "можно экспортировать, а можно — не".
У вас — грубо говоря — получается, что если инкапсуляция обозначается так:
public foo;
public bar;
public baz;
то она есть, а если так
public (foo, bar, baz)
foo;
bar;
baz;
то ее нет. K>>Она чистая по построению (с объяснения этого все мое участие в этой ветке и начинается), так что с момента ее написания. _>Т.е. мы можем спокойно закэшировать пару одинаковых вызовов putStrLn, как и положено с любой чистой функцией? )
Можно ли "закэшировать пару вызовов" функций зависит от того, можно ли закэшировать значение которое она возвращает. Определение чистой функции тут только притом, что мы переходим к рассуждению о свойствах этого самого возвращаемого значения.
Если вы хотите сказать, что
putStrLn "foo" >> putStrLn "foo"
и
let f = putStrLn "foo"in f >> f
это одно и то же — то да, так и есть. K>>У него был пример "чистой" функции, которая изменяет передаваемые ей объекты, без всякой системы упорядочивания этих действий. Это значит, что обсуждаемый закон для такой функции не выполняется. _>Ну так покажите где он там не выполняется...
D я не знаю, но принцип, думаю, понятен:
> let f (i : int ref) a = a + !i;;
val f : i:int ref -> a:int -> int
> let g (i : int ref) b = i := b; b;;
val g : i:int ref -> b:int -> int
> let i = ref 0 in List.map (f i) << List.map (g i) <| [1..10];;
val it : int list = [11; 12; 13; 14; 15; 16; 17; 18; 19; 20]
> let i = ref 0 in List.map (f i << g i) <| [1..10];;
val it : int list = [2; 4; 6; 8; 10; 12; 14; 16; 18; 20]
_>Весьма сомнительно,
Я просто скомпилировал указанный код на хаскеле с ключем -O2 -ddump-simpl -ddump-to-file -dsuppress-all -dsuppress-uniques -dppr-case-as-let
И получил дамп промежуточного представления такого вот вида:
Скрытый текст
\ w w1 ->
let { Vector ipv ipv1 ipv2 ~ _ <- w1 `cast` ... } in
case >=# 0 ipv1 of _ {
False ->
let { I# y ~ _ <- w } in
let { __DEFAULT ~ wild <- indexIntArray# ipv2 ipv } in
case <=# (*# wild wild) y of _ {
False -> True;
True ->
case wild of wild4 {
__DEFAULT ->
case modInt# y wild4 of _ {
__DEFAULT ->
letrec {
$s$wand_loop
$s$wand_loop =
\ sc ->
case >=# sc ipv1 of _ {
False ->
let { __DEFAULT ~ wild6 <- indexIntArray# ipv2 (+# ipv sc) } in
case <=# (*# wild6 wild6) y of _ {
False -> True;
True ->
case wild6 of wild8 {
__DEFAULT ->
case modInt# y wild8 of _ {
__DEFAULT -> $s$wand_loop (+# sc 1);
0 -> False
};
(-1) -> False;
0 -> case divZeroError of wild9 { }
}
};
True -> True
}; } in
$s$wand_loop 1;
0 -> False
};
(-1) -> False;
0 -> case divZeroError of wild5 { }
}
};
True -> True
}
и перевел его обратно в хаскель с некоторыми не влияющими на смысл упрощениями. _>т.к. иначе с чего бы это ваш вариант вычислений с мутабельными данными на Хаскеле в несколько раз более тормознутый, чем вариант на D? )
1) Мы их никогда не запускали на одной машине. так что описанные несколько раз это в основном ваше wishful thinking.
2) Это не весь код, а только часть его — не везде промежуточные данные могут быть устранены оптимизатором.
3) Мало ли что там могло отличаться, к примеру в случае D могли быть i32, а в случае хаскеля i64 и так далее.
4) Мало ли что там могло тормозить? Я этот код не оптимизировал. _>Да и к тому же вы не показали собственно с помощью чего производятся подобные оптимизации или это магия? )))
Это обычные общеполезные оптимизации для ФЯ + правила перезаписи, небольшой пример и объяснение работы которых я уже в этой ветке раньше показывал.
В данном случае это stream fusion, прочесть про него можно здесь: http://community.haskell.org/~duncan/thesis.pdf _>Ооо, наконец то начинает появляться понимание... Кстати, замечу, что неизменяемое значение должно быть таким только на протяжение своей жизни. Так что immutable табличку вполне можно стереть после использования и заменить ту же самую ячейку памяти новыми данными (возможно тоже неизменяемыми).
И? Как это противоречит тому что я говорю?
В любом случае иммутабельность таблицы синусов — это маргинальный юзкейс для иммутабельности. Нормальный юзкейс — это персистентные структуры данных. _>То, что тестирование работы с иммутабельными данными равносильно насилованию менеджера памяти — это исключительно ваш тезис.
Нет, не равносильно, но это один из аспектов. Что неоднократно мной уже разъяснено. Если вы не модифицируете структуру данных на месте, а создаете новую версию — без "насилованию менеджера памяти" тут никак не обойдется, он должен быть к такому готов.
Впрочем, альтернативных предложений о методике измерения поддержки иммутабельных данных от вас все равно не поступает. Вы больше напираете на ненужность. _>Причём в D (в котором есть множество различных способов размещения данных) это вообще выглядит крайне смешно.
Смешное тут разве что то, что вы не спешите демонстрировать эти успехи "различных способов размещения". А разгадка проста — самого подходящего способа размещения иммутабельных структур — хипа с быстрым точным сборщиком — в D как раз и нет. _>Выделение не обязательно делать в динамической памяти. Можно и на стеке и тогда при сворачивание рекурсии в цикл мы можем получить практически точную копию текущего библиотечного кода.
Даже при воспроизведении моего крайне простого примера в пару строк вам возможностей стека уже не будет хватать.
Это если оставить за скобками привычную уже абсурдность измерения скорости работы с динамической памятью ее неиспользованием.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[83]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали: K>Так почему не получится? Есть у вас вложенные классы. Вы можете их обозначать просто с префикосом к имени, если этого достаточно. Можете с помощью имеющихся отношений (вы же сами ссылку на пример и дали) их обозначать, если префикса недостаточно. И этих возможностей хватит чтоб строить диаграммы для хаскель-кода.
Я же говорю, что нет этого отношения ни в стандарте, ни в известных инструментах для UML. ) K>Так в чем бредовость? Чем это бредовее отображения на диаграмме класса у которого есть методы и вложенные классы?
Потому что, ради того, чтобы всунуть модули в диаграмму классов (хотя им явно самое место в диаграмме пакетов), вы предлагаете обозначать одинаковыми значками абсолютно разные сущности. Т.е. они разные не просто в рамках какой-то программы, а вообще по определению — имеют разные возможные типы связей, разные виды жизни, разное содержимое и в принципе концептуально разные. K>Мы так и не выяснили, что туда нужно добавлять, если там и так уже все необходимое есть. K>Впрочем, "так и будет оставаться" — это верно, потому что популярность такого рода диаграмм в ФП-комьюнити крайне низкая и почти никто в этом направлении не работает.
Естественно, т.к. от того "что есть" (в смысле диаграмм) никакой особой пользы не видно. А в итоге получаем отсутствие инструментов помощи в проектирование для подобных языков. Да и средства документирования выглядят не так выразительно. K>Как никаких модулей? Модули вот здесь "можно экспортировать, а можно — не". K>У вас — грубо говоря — получается, что если инкапсуляция обозначается так: K>
K>public foo;
K>public bar;
K>public baz;
K>
K>то она есть, а если так K>
K>public (foo, bar, baz)
K>foo;
K>bar;
K>baz;
K>
K>то ее нет.
Нет, дело не в этом. Вопрос в том, кто у нас является единицей инкапсуляции в Хаскеле. Судя по вашим ответам, это алгебраический тип данных. А модули соответственно служат лишь инструментом отделения публичной части этих типов от приватной. И это больше всего напоминает древнюю схему Паскаля или даже вообще С. K>Можно ли "закэшировать пару вызовов" функций зависит от того, можно ли закэшировать значение которое она возвращает. Определение чистой функции тут только притом, что мы переходим к рассуждению о свойствах этого самого возвращаемого значения. K>Если вы хотите сказать, что K>
K>putStrLn "foo" >> putStrLn "foo"
K>
K>и K>
K>let f = putStrLn "foo"in f >> f
K>
K>это одно и то же — то да, так и есть.
Уууу какая демагогия пошла... А без монад? ) K>D я не знаю, но принцип, думаю, понятен: K>
>> let f (i : int ref) a = a + !i;;
K>val f : i:int ref -> a:int -> int
>> let g (i : int ref) b = i := b; b;;
K>val g : i:int ref -> b:int -> int
>> let i = ref 0 in List.map (f i) << List.map (g i) <| [1..10];;
K>val it : int list = [11; 12; 13; 14; 15; 16; 17; 18; 19; 20]
>> let i = ref 0 in List.map (f i << g i) <| [1..10];;
K>val it : int list = [2; 4; 6; 8; 10; 12; 14; 16; 18; 20]
K>
Нет, это вообще не то. Здесь вы доказываете, что map (f(p, g_var) . f(q, g_var)) != map f(p, g_var) . map f(q , g_var), что в общем то и так очевидно. Для реального доказательства вам надо передать в map именно те функции, которые служат аналогами чистых функций в D. K>Я просто скомпилировал указанный код на хаскеле с ключем -O2 -ddump-simpl -ddump-to-file -dsuppress-all -dsuppress-uniques -dppr-case-as-let K>И получил дамп промежуточного представления такого вот вида: K>
Скрытый текст
K>
K> \ w w1 ->
K> let { Vector ipv ipv1 ipv2 ~ _ <- w1 `cast` ... } in
K> case >=# 0 ipv1 of _ {
K> False ->
K> let { I# y ~ _ <- w } in
K> let { __DEFAULT ~ wild <- indexIntArray# ipv2 ipv } in
K> case <=# (*# wild wild) y of _ {
K> False -> True;
K> True ->
K> case wild of wild4 {
K> __DEFAULT ->
K> case modInt# y wild4 of _ {
K> __DEFAULT ->
K> letrec {
K> $s$wand_loop
K> $s$wand_loop =
K> \ sc ->
K> case >=# sc ipv1 of _ {
K> False ->
K> let { __DEFAULT ~ wild6 <- indexIntArray# ipv2 (+# ipv sc) } in
K> case <=# (*# wild6 wild6) y of _ {
K> False -> True;
K> True ->
K> case wild6 of wild8 {
K> __DEFAULT ->
K> case modInt# y wild8 of _ {
K> __DEFAULT -> $s$wand_loop (+# sc 1);
K> 0 -> False
K> };
K> (-1) -> False;
K> 0 -> case divZeroError of wild9 { }
K> }
K> };
K> True -> True
K> }; } in
K> $s$wand_loop 1;
K> 0 -> False
K> };
K> (-1) -> False;
K> 0 -> case divZeroError of wild5 { }
K> }
K> };
K> True -> True
K> }
K>
K>и перевел его обратно в хаскель с некоторыми не влияющими на смысл упрощениями.
О, а тогда может быть можно сделать аналогичное и для вашего примера с мутабельными данными? ) K>Нет, не равносильно, но это один из аспектов. Что неоднократно мной уже разъяснено. Если вы не модифицируете структуру данных на месте, а создаете новую версию — без "насилованию менеджера памяти" тут никак не обойдется, он должен быть к такому готов. K>Впрочем, альтернативных предложений о методике измерения поддержки иммутабельных данных от вас все равно не поступает. Вы больше напираете на ненужность.
Совершенно верно) Для подобных случаев я точно никогда не буду использовать иммутабельные данные. Ни в каком языке. ) K>Смешное тут разве что то, что вы не спешите демонстрировать эти успехи "различных способов размещения". А разгадка проста — самого подходящего способа размещения иммутабельных структур — хипа с быстрым точным сборщиком — в D как раз и нет.
А что тут демонстрировать то? ) Я например стараюсь использовать стековые данные везде, кроме разве что коллекций или больших блоков данных. Причём "стараюсь" — это сильно сказано, т.к. обычно их использовать наоборот приятнее: писать "A a;" удобнее, чем "A* a=new A;" и т.д. и т.п. Ну а кроме стековых, в D мы можем использовать опять же динамическую память, но без GC... K>Даже при воспроизведении моего крайне простого примера в пару строк вам возможностей стека уже не будет хватать. K>Это если оставить за скобками привычную уже абсурдность измерения скорости работы с динамической памятью ее неиспользованием.
Так мы же подразумеваем, что рекурсия свернётся в цикл компилятором. Так что всё там будет нормально со стеком, в точности как и в текущем коде с мутабельными индексами. Весь вопрос же только в них, т.к. главный массив у нас по любому остаётся в динамической памяти.
Re[84]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Я же говорю, что нет этого отношения ни в стандарте, ни в известных инструментах для UML. )
Прямо по той ссылке, которую вы же сами и давали, написано, что для этого можно использовать обычное отношение композиции.
_>Потому что, ради того, чтобы всунуть модули в диаграмму классов (хотя им явно самое место в диаграмме пакетов),
Вовсе нет. На диаграмме пакетов место для пакетов. А место модулей на той же диаграмме, на которой место для классов.
_> вы предлагаете обозначать одинаковыми значками абсолютно разные сущности. Т.е. они разные не просто в рамках какой-то программы, а вообще по определению — имеют разные возможные типы связей, разные виды жизни, разное содержимое и в принципе концептуально разные.
Совершенно верно. Точно так же как одними значками (но с разными стереотипами) обозначаются "имеющие разные возможные типы связей, разные виды жизни, разное содержимое и в принципе концептуально разные" статические классы, классы и интерфейсы.
_>Естественно, т.к. от того "что есть" (в смысле диаграмм) никакой особой пользы не видно.
Мне с этим сложно спорить, потому что я не вижу от таких вещей заметной пользы безотносительно языка.
_>А в итоге получаем отсутствие инструментов помощи в проектирование для подобных языков. Да и средства документирования выглядят не так выразительно.
Не вижу проблемы в использовании существующих инструментов.
_>Нет, дело не в этом. Вопрос в том, кто у нас является единицей инкапсуляции в Хаскеле. Судя по вашим ответам, это алгебраический тип данных.
Единицей инкапсуляции являются значения, функции, конструкторы, точно так же, как в яве поля, методы, конструкторы. Разница заключается, в основном, в синтаксическом оформлении декларации публичности/приватности.
_>А модули соответственно служат лишь инструментом отделения публичной части этих типов от приватной. И это больше всего напоминает древнюю схему Паскаля или даже вообще С.
Не вижу смысла критиковать систему модулей паскаля, критикуйте систему модулей хаскеля (они не тождественны), если вы про систему модулей хаскеля ничего не знаете — просто составьте представление — это не долго, это не какие-нибудь мл-модули. В С же вообще обсуждать нечего.
_>Уууу какая демагогия пошла...
Где тут демагогия?
_>А без монад? )
Легко:
putStrLn "foo" `thenIO` putStrLn "foo"let f = putStrLn "foo"in f `thenIO` f
_>Нет, это вообще не то. Здесь вы доказываете, что map (f(p, g_var) . f(q, g_var)) != map f(p, g_var) . map f(q , g_var), что в общем то и так очевидно.
Ну так в этом и вопрос: как написать map, в который такие функции не передать?
_>Для реального доказательства вам надо передать в map именно те функции, которые служат аналогами чистых функций в D.
Не понял.
auto p(ref int v) pure {return ++v+1;}
в ди чистая функция, а
auto p(ref int v, int x) pure {v = x; return x;}
нет?
_>О, а тогда может быть можно сделать аналогичное и для вашего примера с мутабельными данными? )
Если имеется в виду сырой дамп промежуточного представления, то легко: http://files.rsdn.ru/46291/Main.dump-simpl.txt
Если имеется в виду "приглаженная" версия, то нет — слишком много кода, мне лень его править.
_>Совершенно верно) Для подобных случаев я точно никогда не буду использовать иммутабельные данные. Ни в каком языке. )
Т.е. персистентные структуры данных вы ни в каком языке использовать не будете?
Вообще, я могу только позавидовать вашей неприхотливости. Но многие люди отличаются от вас, когда они слышать что "есть поддержка иммутабельности" — они ожидают, что какая-то поддержка действительно есть.
_>А что тут демонстрировать то?
Демонстрировать работающий пример, отвечающий условиям задачи.
_>в D мы можем использовать опять же динамическую память, но без GC...
О да, для работы с иммутабельными данными такая возможность конечно же крайне полезна.
_>Так мы же подразумеваем, что рекурсия свернётся в цикл компилятором. Так что всё там будет нормально со стеком, в точности как и в текущем коде с мутабельными индексами. Весь вопрос же только в них, т.к. главный массив у нас по любому остаётся в динамической памяти.
Посмотрите внимательнее на мой пример с иммутабельными данными.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[85]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>Прямо по той ссылке, которую вы же сами и давали, написано, что для этого можно использовать обычное отношение композиции.
Нет, это другое. Просто чаще всего экземпляры вложенного класса ещё и содержатся внутри внешнего (типа как List::Node), т.е. требуется и композицию.
K>Вовсе нет. На диаграмме пакетов место для пакетов. А место модулей на той же диаграмме, на которой место для классов.
Ещё раз: если вы решите обозначать модули значком класса (что довольно бредово — см. ниже), то тогда АТД вы не можете обозначать так же, т.к. нельзя обозначать одним и тем же значком сущности с абсолютно разными свойствами, временем жизни и т.п.
K>Совершенно верно. Точно так же как одними значками (но с разными стереотипами) обозначаются "имеющие разные возможные типы связей, разные виды жизни, разное содержимое и в принципе концептуально разные" статические классы, классы и интерфейсы.
Вы не понимаете ключевой нюанс. Если какой-то класс имеет особое поведение (в данном случае это означает, что не имеет некоторых обычных связей) в данной конкретной программе, то это совершенно не означает, что мы не можем пририсовать их (с полным сохранением корректности). Это особо хорошо чувствуется на инструментах, позволяющих генерировать исходный код по диаграмме. К примеру, если у вас находится в программе статический класс и вы пририсуете на диаграммке создание нескольких его экземпляров в другом классе, то при этом всё равно сгенерируется компилируемый исходник. А что будет в случае модулей хаскеля на подобном месте? )))
В общем не стоит придумывать ерунду. Значок класса в UML предполагает наличие набора определённых свойств, которым модули хаскеля очевидно не удовлетворяют. Этим свойствам более менее удовлетворяют АДТ хаскеля и вот их действительно можно пытаться рисовать на диаграммке классов (что мы и видели в паре примеров выше). Но такие диаграммки естественно будут давать не полное представление о программе, о чём мы уже говорили выше.
K>Единицей инкапсуляции являются значения, функции, конструкторы, точно так же, как в яве поля, методы, конструкторы. Разница заключается, в основном, в синтаксическом оформлении декларации публичности/приватности.
Это вы говорите "что" инкапсулируется. А я обсуждаю вопрос "куда". В ООП языках это класс. В Хаскеле, как я понимаю, АТД.
K>Не вижу смысла критиковать систему модулей паскаля, критикуйте систему модулей хаскеля (они не тождественны), если вы про систему модулей хаскеля ничего не знаете — просто составьте представление — это не долго, это не какие-нибудь мл-модули. В С же вообще обсуждать нечего.
Я критикую не систему модулей хаскеля (не вижу в ней ничего плохого, если говорить только о собственно модулях), а попытку введения инкапсуляции (для АТД) с помощью них. Вот это выглядит очень убого и напоминает технологии древного Паскаля или вообще C. А сами модули то очень даже ничего, удобные.
_>>Нет, это вообще не то. Здесь вы доказываете, что map (f(p, g_var) . f(q, g_var)) != map f(p, g_var) . map f(q , g_var), что в общем то и так очевидно. K>Ну так в этом и вопрос: как написать map, в который такие функции не передать?
Ээээ с чего это у нас должны быть какие-то ограничения на map? Она то как раз должна работать с любыми функциями и вполне нормально, если то равенство не будет выполняться для нечистых (в значение D).
Вы то пытаетесь доказать, что оно не выполняется для чистых функций. Ну так вот и доказывайте...
K>Не понял. K>
K>auto p(ref int v) pure {return ++v+1;}
K>
K>в ди чистая функция, а K>
K>auto p(ref int v, int x) pure {v = x; return x;}
K>
K>нет?
p и q в вашем коде на Хаскеле действительно являются нормальными аналогами чистых функций в D. Только вот в своём примере вы передаёте в map не p или q, а совсем другие функции...
K>Если имеется в виду сырой дамп промежуточного представления, то легко: http://files.rsdn.ru/46291/Main.dump-simpl.txt K>Если имеется в виду "приглаженная" версия, то нет — слишком много кода, мне лень его править.
Не, такое не разберу. А может вы тогда просто глянете сколько там всего у нас циклов (рекурсий)? В идеале должно быть ровно две: один по возрастающим целым числам и внутри второй по нашему массиву. И всё. Именно такой код генерирует в итоге та одна моя строчка на D. И вы анонсировали, что на Хаскеле будет тоже самое благодаря хитрым оптимизациям...
_>>Совершенно верно) Для подобных случаев я точно никогда не буду использовать иммутабельные данные. Ни в каком языке. ) K>Т.е. персистентные структуры данных вы ни в каком языке использовать не будете?
Почему же не буду? ) Я их постоянно использую, если они таковые по условию задачи. Однако, если я вижу, что какие-то данные имеют тенденцию к изменению, то я естественно не буду делать их иммутабельными и порождать новые копии при изменение, а просто использую мутабельные структуры.
_>>в D мы можем использовать опять же динамическую память, но без GC... K>О да, для работы с иммутабельными данными такая возможность конечно же крайне полезна.
В D и для таких сущностей вполне можно с пользой использовать иммутабельность. )))
Re[84]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
K>>Так в чем бредовость? Чем это бредовее отображения на диаграмме класса у которого есть методы и вложенные классы?
_>Потому что, ради того, чтобы всунуть модули в диаграмму классов (хотя им явно самое место в диаграмме пакетов), вы предлагаете обозначать одинаковыми значками абсолютно разные сущности. Т.е. они разные не просто в рамках какой-то программы, а вообще по определению — имеют разные возможные типы связей, разные виды жизни, разное содержимое и в принципе концептуально разные.
Вообще то классика ООП утверждает, что классы в ооп это
1 модули
2 типы
Отсюда все их преимущества и недостатки
В свою очередь классика ФП разделяет эти вещи — типы отдельно от модулей. Что характерно, и там и там в основе типов лежит АТД. Отсюда ясно что оба подхода могут и используют один и тот же UML
Re[85]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Вообще то классика ООП утверждает, что классы в ооп это I>1 модули I>2 типы
I>Отсюда все их преимущества и недостатки
Всё правильно и это единая неделимая сущность, которая имеет определённый набор свойств (отражённый в UML соответствующими правилами).
I>В свою очередь классика ФП разделяет эти вещи — типы отдельно от модулей. Что характерно, и там и там в основе типов лежит АТД. Отсюда ясно что оба подхода могут и используют один и тот же UML
Да, безусловно можно разработать некий вид диаграмм (и даже включить их в стандарт UML), которые будут отлично отображать эти две сущности. Могу даже предложить для этого взять за основу диаграмму классов, в которой чуть поменять свойства для значка класса, добавить новый значок для модулей, и соответственно пересмотреть набор возможных видов связей.
Т.е. основная моя мысль в том, что в теории то без проблем, а на практике (в существующих стандартах и соответственно инструментах) фигово у чистых ФЯ с этим делом.
Re[86]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>Нет, это другое. Просто чаще всего экземпляры вложенного класса ещё и содержатся внутри внешнего (типа как List::Node), т.е. требуется и композицию.
Допустим, что у вас статический класс и вложенный в него обычный. Как вы их покажете на диаграмме?
_>Ещё раз: если вы решите обозначать модули значком класса (что довольно бредово — см. ниже), то тогда АТД вы не можете обозначать так же, т.к. нельзя обозначать одним и тем же значком сущности с абсолютно разными свойствами, временем жизни и т.п.
Можно. Например статический класс и не статический класс.
_>А что будет в случае модулей хаскеля на подобном месте?
Очевидно, что ничего. Мы говорим об использовании инструментов, которые не предназначены специально для хаскеля. Разумеется, код на хаскеле такие инструменты не генерируют.
_>В общем не стоит придумывать ерунду. Значок класса в UML предполагает наличие набора определённых свойств, которым модули хаскеля очевидно не удовлетворяют.
Каким свойствам удовлетворяют статические классы и не удовлетворяют модули?
_>Этим свойствам более менее удовлетворяют АДТ хаскеля и вот их действительно можно пытаться рисовать на диаграммке классов (что мы и видели в паре примеров выше). Но такие диаграммки естественно будут давать не полное представление о программе, о чём мы уже говорили выше.
Чем неполнота представления о программе на хаскеле будет отличатся от неполноты представления о программе на Яве?
_>Это вы говорите "что" инкапсулируется. А я обсуждаю вопрос "куда". В ООП языках это класс. В Хаскеле, как я понимаю, АТД.
В хаскеле "куда" — это модуль. Потому, что класс является модулем (первоклассным), а АлгТД — не является.
_>Я критикую не систему модулей хаскеля (не вижу в ней ничего плохого, если говорить только о собственно модулях), а попытку введения инкапсуляции (для АТД) с помощью них. Вот это выглядит очень убого и напоминает технологии древного Паскаля или вообще C. А сами модули то очень даже ничего, удобные.
Вообще ничего не понятно. Если вам ими удобно пользоваться, то в чем претензии к убогости тогда. Что конкретно плохо — вы можете пример показать, а не говорить что что-то просто убого, только непонятно что? И в чем сходство с Си? Как наличие модулей может быть сходным с отсутствием модулей?
_>Ээээ с чего это у нас должны быть какие-то ограничения на map?
Должны потому что такие уж свойства у map. И без "ограничения" на функции такие свойства не выполняются.
Я же с самого начала и спросил: "Ну вот у вас есть функция map, которая принимает какую-то функцию p или q. Как можно проконтролировать эффекты, чтоб выполнялось map (p . q) = map p . map q". И функции в языке для того и нужны, чтоб такие вот законы могли выполнятся.
_>Она то как раз должна работать с любыми функциями и вполне нормально, если то равенство не будет выполняться для нечистых (в значение D).
Нет, это не нормально.
_>p и q в вашем коде на Хаскеле действительно являются нормальными аналогами чистых функций в D. Только вот в своём примере вы передаёте в map не p или q, а совсем другие функции...
Которые в D не могут быть чистыми? Ну пусть тогда список к которому map применяется заполнен объектами со ссылкой на один и тот же мутабельный объект.
_>Не, такое не разберу. А может вы тогда просто глянете сколько там всего у нас циклов (рекурсий)? В идеале должно быть ровно две: один по возрастающим целым числам и внутри второй по нашему массиву. И всё. Именно такой код генерирует в итоге та одна моя строчка на D. И вы анонсировали, что на Хаскеле будет тоже самое благодаря хитрым оптимизациям...
? Так я показал во что превращается ее аналог.
_>Почему же не буду? ) Я их постоянно использую, если они таковые по условию задачи. Однако, если я вижу, что какие-то данные имеют тенденцию к изменению, то я естественно не буду делать их иммутабельными и порождать новые копии при изменение, а просто использую мутабельные структуры.
Вы точно понимаете, что такое персистентные структуры данных? Они собственно и предназначены для хранения "того, что имеет тенденцию к изменению", а не для таблиц синусов.
_>В D и для таких сущностей вполне можно с пользой использовать иммутабельность. )))
Нельзя. Сборщик мусора не потянет.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[3]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Darooma, Вы писали:
D>а я даже понял. они служат просто для сцепления, комбинирования методов.
Наверное все-же нет, обычные делегаты служат для сцепления методов.
Я тоже не очень четко понимаю что такое монады (поэтому все ниженаписанное может содержать ошибки — поправляйте если что). Проблема еще в том что в русском языке нет (как мне кажется) достаточно общих и в то же время всем понятных слов для выражения того эффекта, который дают монады. Все что говорится обычно — лишь частные случаи.
Монада — это конструкция языка. Такая же как условное выполнение, функция, класс, обработка исключений. Т.е. это вещь, относящаяся к парадигмам программирования. Некоторые говорят что это "паттерн проектирования", но я считаю что это все-же нечно большее.
Мы привыкли к императивному подходу, включающему в себя структурное и процедурное программирование. Для нас программа — это дерево, состоящее из блоков — последовательного выполнения, условных операторов, циклов, вызовов функций. ООП и известная большинству часть ФП ничего в этой парадигме не затронули: ООП в основном относится к данным, к способу их структурирования и инкапсуляции, ФП-расширения основных языков позволяют передавать функции как аргументы и возвращать их как значения — но концептуально все это базиурется на незыблемом императивном фундаменте.
Я не знаком с декларативными языками (в частности с haskell), но как я понимаю, основной смысл декларативной парадигмы — описательный стиль, программа описывает предметную область и решаемую задачу, а не инструкции "как сделать то-то". Это такая своеобразная большая система уравнений (?), а не пошаговый алгоритм. Поэтому в декларативных языках понятия "последовательность выполнения кода" просто нет. Но компьютер есть компьютер, в нем есть взаимодействие с пользователем, с другими программами и сервисами... все это императивное по определению, и с этим нужно как-то взаимодействовать.
Например, если мы хотим разделить два числа, то в императивном языке простейшая программа последовательно запросит делимое, делитель и выведет результат деления. Последовательность вызова функции получения числа заложена в самой сути императивной программы, в последовательности строчек кода.
В декларативном языке такое просто невозможно: даже если есть некая системная функция типа "readIntFromConsole()": нет никакой гарантии, что она будет вызвана в правильном порядке, т.е. сначала для делимого, а затем для делителя. Монада (с названием "IO") — это сущность, вводящая в декларативную "систему уравнений" фрагмент последовательного выполнения, гарантирующая что одно выражение (получение делимого с консоли) будет вычислено раньше, чем другое (получение делителя с консоли).
Монады естественны в декларативной среде. Декларативная среда — "нулевая" с точки зрения парадигмы выполнения кода; там последовательность выполнения кода вообще не определена. В ней монады фактически реализуют некоторые конструкции, алгоритмы и паттерны проектирования, которые в классической императивной среде реализуются совершенно разными способами, начиная от языковых конструкций. Но это не значит, что монады не могут существовать в "ненулевой" императивной среде. Некоторые из них (типа "IO") будут просто не нужны, зато другие предположительно дадут очень интересные эффекты, суть которых — в неявном встраивании в привычную императивную последовательность выполнения кода некоторых дополнительных действий. С некоторой точки зрения, монада, включенная в одно единственное место большого длинного выражения, способна перестроить все это выражение целиком.
Например "Maybe", реализующая прозрачную работу с объектами, обладающими недействительным состоянием "Nothing". В обычном императивном коде это могут быть обычные указатели, которые могут указывать как на объект, так и на NULL. Раньше нужны были проверки: если указатель не равен NULL, делаем с ним что-то... С "Maybe" такие проверки можно было бы пропустить: монада добавила бы в привычный императивный код некоторый дополнительный слой проверок таким образом, что для значения "Nothing" никаких действий бы не выполнялось.
Maybe<int> x1 = 10, x2 = Nothing;
int y = x1 + 2; // y == 12
x1 = x2 + 2; // x1 == nothing; поскольку x1 - maybe, все происходит молча
y = x2 + 2; // y == nothing; но y - не maybe, можем ничего не делать (y останется равным 12), или лучше бросить исключение?
"List" также перестраивает последовательность выполнения — там, где раньше было действие с одним значением, List обеспечивает выполнение этого действия с каждым значением из списка. Это принципиально отличается от всего, что мы видели в императивных языках.
Re[87]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>Допустим, что у вас статический класс и вложенный в него обычный. Как вы их покажете на диаграмме?
Обычно это будет просто префикс (через ::) в имени вложенного. Если этот внешний класс действительно статический и не содержит экземпляров внутреннего.
K>Можно. Например статический класс и не статический класс.
Ни в UML ни в C++ и т.п. ООП языках нет такой сущности как "статический класс". Есть просто классы с по сути одинаковыми для всех этих языков свойствами (максимум разница в наличие множественного наследования). Вот статические методы/свойства в языках (и в UML кстати) есть... Ну и вроде как-то принято называть класс, состоящий только из них, статическим. Но при этом он на самом деле обладает всеми свойствами обычного класса. Мы мы можем и не статические члены в него добавить и экземпляры его завести и т.п.
K>Очевидно, что ничего. Мы говорим об использовании инструментов, которые не предназначены специально для хаскеля. Разумеется, код на хаскеле такие инструменты не генерируют.
Собственно об этом речь. Т.е. естественно не про генерацию кода, а в про вообще отсутствие поддержки языков типа Хаскеля в подобных инструментах.
K>Чем неполнота представления о программе на хаскеле будет отличатся от неполноты представления о программе на Яве?
В Хаскеле (в отличие от Явы) значительная часть сущности программы может задаваться свободными функциями.
K>В хаскеле "куда" — это модуль. Потому, что класс является модулем (первоклассным), а АлгТД — не является.
Тогда мы снова возвращаемся к вопросу о массивах модулей... В общем вам надо выбрать один из двух вариантов:
1. Единицей инкапсуляции в Хаскеле являются АТД. Тогда у нас получаются убогие средства для инкапсуляции — через внешнюю сущность (модули) делящую код на части (интерфейс/реализацию), как в древнем Паскале/С.
2. Единицей инкапсуляции в Хаскеле являются модули. Но тогда у нас вообще всё плохо — нельзя получить например массив сущностей, инкапсулирующих какие-то свойства.
По моим ощущениям правилен вариант 1, но возможно вам, как практику Хаскеля, виднее. Всё равно оба варианта фиговые. )))
K>Вообще ничего не понятно. Если вам ими удобно пользоваться, то в чем претензии к убогости тогда. Что конкретно плохо — вы можете пример показать, а не говорить что что-то просто убого, только непонятно что? И в чем сходство с Си? Как наличие модулей может быть сходным с отсутствием модулей?
Напоминаю, мы говорим об инкапсуляции, а не о системе модулей. Да, и если что, в ООП языках инкапсуляция реализуется независимо от системы модулей. Собственно она есть в полную силу и в языках, по сути вообще без модульной системы (типа C++).
K>Должны потому что такие уж свойства у map. И без "ограничения" на функции такие свойства не выполняются. K>Я же с самого начала и спросил: "Ну вот у вас есть функция map, которая принимает какую-то функцию p или q. Как можно проконтролировать эффекты, чтоб выполнялось map (p . q) = map p . map q". И функции в языке для того и нужны, чтоб такие вот законы могли выполнятся.
Это у вас какой-то свой особый map. А в большинстве языков программирования это правило не выполняется для map и произвольных функций.
_>>Она то как раз должна работать с любыми функциями и вполне нормально, если то равенство не будет выполняться для нечистых (в значение D). K>Нет, это не нормально.
Почему это? )
K>Которые в D не могут быть чистыми? Ну пусть тогда список к которому map применяется заполнен объектами со ссылкой на один и тот же мутабельный объект.
Да, совершенно верно. Функции, передаваемые в вашем примере в map не могут быть чистыми на D.
А на счёт массива ссылок на один и тот же объект... В D такое тоже не пройдёт (в том смысле что всё будет работать и при этом наше равенство будет сохраняться), т.к. в результате map'a в любом случае будет получаться массив с разными ссылками. Я же говорю, что без перехода на уровень игр с указателями у вас ничего не выйдет.
K>Какая "та строчка"? Та, что тут http://rsdn.ru/forum/philosophy/5624883.1
Это у вас показан вариант с иммутабельными данными (что вообще не интересно, т.к. дико отстаёт по быстродействию), а нужен как раз с мутабельными. Причём в случае Хаскеля, как я понимаю, тут как раз может быть большая разница в коде...
K>Вы точно понимаете, что такое персистентные структуры данных? Они собственно и предназначены для хранения "того, что имеет тенденцию к изменению", а не для таблиц синусов.
Нет, не так. Скорее для того, что имеет тенденцию к изменению, и плюс нам необходимо сохранять доступ к старым версиям. Это уже гораздо более редкая задачка (по сравнению с просто мутабельными или даже иммутабельными данными). И естественно, если она мне встретится, то я могу использовать коллекцию иммутабельных данных или же специальную персистентную структуру, если требуется оптимизация по быстродействию.
А вот что я точно никогда не буду делать, так это пытаться использовать наборы иммутабельных данных (пусть даже и оптимизированных с помощью персистентной структуры) для представления нормальных мутабельных данных (для которых не надо хранить старые версии), которых собственно большинство в обычных программах.
_>>В D и для таких сущностей вполне можно с пользой использовать иммутабельность. ))) K>Нельзя. Сборщик мусора не потянет.
Вообще то в том контексте разговора под сущностью подразумевалась динамическая память, выделенная в ручную (а не через GC), так что ответ выглядит мягко говоря странно. Но я уже и так понял, что для вас GC является чуть не главным вообще. На что мне, привыкшему к работе через стек и RAII, вообще очень забавно смотреть.
Re[4]: Есть ли вещи, которые вы прницпиально не понимаете...
У вас просто великолепное понимание, особенно для незнакомого с декларативными языками. Только вот один важный вывод из ваших же мыслей вы упустили. Как вы правильно заметили, монады задают императивные конструкции в декларативных языках. Т.е. они являясь лишь небольшой частью языка, пытаются выполнить ту же задачу, для реализации которой предназначена вся мощь современных императивных языков. Т.е. грубо говоря монады являются убогонькой альтернативой самим императивным языкам. И соответственно использовать их здесь в явном виде довольно глупо (но совсем не сложно, как было показано в начале этой темки).
Да, кстати, поскольку монады задают не произвольный императивный код, а с весьма специфическими параметрами, то их можно изредка встретить в обычном императивном коде. Т.е. это не значит, что авторы планировали создать монаду. Они просто писали обычный императивный код под специфическую задачку и в результате у них нарисовалась типичная монада (им от этого правда ни холодно, ни жарко). Как примеры:
— вариации на тему maybe (std.optional в C++, optional в C# и Swift)
— различные продолжения (конструкции future.then в реализации многопоточности во многих языках)
— различные дипазоны (boost.range в C++, std.range в D, IEnumerable в C#, Sequence в Swift)
и много ещё чего может быть, типа реализаций различных парсеров и т.п.
Так что в принципе это вещь естественная для специфических задачек. А убогость соответственно заключается в том, что работая только с монадами, мы должны писать вообще весь императивный код только таким способом.
Re[88]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
K>>Допустим, что у вас статический класс и вложенный в него обычный. Как вы их покажете на диаграмме? _>Обычно это будет просто префикс (через ::) в имени вложенного. Если этот внешний класс действительно статический и не содержит экземпляров внутреннего.
ОК. А где на этой диаграмме будут статические методы статического класса?
_>Ни в UML ни в C++ и т.п. ООП языках нет такой сущности как "статический класс".
В C# есть статический класс. Что, C# тоже не совместим с UML, как и хаскель?
_>Мы мы можем и не статические члены в него добавить и экземпляры его завести и т.п.
Нет, не можем. Если мы попытаемся добавить нестатический член в статический класс в C# будет ошибка компиляции.
_>Собственно об этом речь. Т.е. естественно не про генерацию кода, а в про вообще отсутствие поддержки языков типа Хаскеля в подобных инструментах.
Поддержки генерации хаскель кода разумеется нет, а что не хватает в UML чтоб можно было схемы для хаскельного кода строить вы все никак не можете показать.
_>В Хаскеле (в отличие от Явы) значительная часть сущности программы может задаваться свободными функциями.
В хаскеле не бывает свободных функций, они всегда находятся, по крайней мере, в каком-то модуле. И если вы не опишете никакого модуля — они будут "публичными членами" модуля Main.
_>Тогда мы снова возвращаемся к вопросу о массивах модулей...
Не нужно возвращается к массиву модулей, потому что нет связи между тем в чем что-то инкапсулируется и в чем что-то хранится. Да, классы в ООП совмещают несколько функций, которые в других языках могут не совмещаться в одном инструменте, но это не значит, что инкапсуляции в них нет — с чего вы этот разговор и начали.
_>Напоминаю, мы говорим об инкапсуляции, а не о системе модулей. Да, и если что, в ООП языках инкапсуляция реализуется независимо от системы модулей. Собственно она есть в полную силу и в языках, по сути вообще без модульной системы (типа C++).
Собственно в том, что в классах можно что-то инкапсулировать и является проявлением того, что классы обладают некоторыми свойствами/функциями модулей. Поэтому, если про C можно точно сказать, что модулей в нем нет — то в случае C++ есть нюанс — потому что классы — это тоже, в общем-то, модули (схемеры бы сказали, наверное, "модули для бедных").
_>Это у вас какой-то свой особый map. А в большинстве языков программирования это правило не выполняется для map и произвольных функций.
Нет, мап самый обычный. Если вашу дишную чистоту нельзя использовать для того, чтоб функция могла проконтролировать, что в нее именно чистые функции передаются — то никаким надмножеством чистоты в математическом смысле она не является.
K>>Нет, это не нормально. _>Почему это? )
Видите ли, абстракция должна иметь какие-то свойства, иначе ее нельзя использовать без "протекания". В ФП, где абстракции используют нешуточным образом, это особенно очевидно и важно.
В большинстве языков программирования, конечно, никаких удобств для ФП нет — ну, тем больше головной боли для программиста, который вдруг решит на них фп-код писать.
Впрочем, позиция понятна, очередная колбаса, потребности в которой в вашем дишном коммунизме нет.
_>А на счёт массива ссылок на один и тот же объект... В D такое тоже не пройдёт (в том смысле что всё будет работать и при этом наше равенство будет сохраняться), т.к. в результате map'a в любом случае будет получаться массив с разными ссылками.
Каким образом это можно обеспечить, если "чистые" функции могут изменяемые данные получить?
_>Это у вас показан вариант с иммутабельными данными (что вообще не интересно, т.к. дико отстаёт по быстродействию), а нужен как раз с мутабельными. Причём в случае Хаскеля, как я понимаю, тут как раз может быть большая разница в коде...
Это фрагмент кода, из примера с изменяемым буфером. Который демонстрирует как раз то, о чем я говорил — устранение ненужных промежуточных структур. Код на ди, как я понял, так не оптимизируется, вы заменили конвейер на одну функцию find вручную. Разумеется все промежуточные данные в моем примере иммутабельные. Ну так о них и разговор.
Когда мы с вами обсуждали — наоборот — поддержку мутабельности в хаскеле — я вам показывал устранение ненужных копирований при работе с мутабельными массивами.
_>Нет, не так. Скорее для того, что имеет тенденцию к изменению, и плюс нам необходимо сохранять доступ к старым версиям. Это уже гораздо более редкая задачка (по сравнению с просто мутабельными или даже иммутабельными данными). И естественно, если она мне встретится, то я могу использовать коллекцию иммутабельных данных или же специальную персистентную структуру, если требуется оптимизация по быстродействию.
Ну правильно, если у вас персистентные структуры слишком тормозят, вы будете их использовать только в крайнем случае. Но код, который с персистентными структурами работает обычно понятнее и меньше ошибок содержит. Поэтому в языке с поддержкой иммутабельности такие структуры применяют просто для удобства программиста.
_>А вот что я точно никогда не буду делать, так это пытаться использовать наборы иммутабельных данных (пусть даже и оптимизированных с помощью персистентной структуры) для представления нормальных мутабельных данных (для которых не надо хранить старые версии), которых собственно большинство в обычных программах.
Конечно. Ведь и эту колбасу не завезли. Значит и потребности нет.
_>Вообще то в том контексте разговора под сущностью подразумевалась динамическая память, выделенная в ручную (а не через GC),
Так я на это сразу сказал, как здорово при таком подходе персистентные структуры использовать. Может ирония была не достаточно заметна?
_>я уже и так понял, что для вас GC является чуть не главным вообще.
Повторяю в очередной раз. Это один из двух важных аспектов. Другой — устранение промежуточных данных на этапе компиляции. Если есть и то и другое — иммутабельностью можно даже начинать пользоваться, а не обосновывать, почему без нее можно обойтись.
Каждый раз, когда я говорю, что при работе с персистентными структурами будет выделятся куча объектов в памяти, в том числе и много короткоживущих, вы скипаете текст и в очередной раз удивляетесь — чего это я такое значение сборщику мусора придаю. Попробуйте связать одно с другим. Связь есть — точно вам говорю.
_>На что мне, привыкшему к работе через стек и RAII, вообще очень забавно смотреть.
Ничего удивительного. Если для вас поддержка иммутабельности равна заверениям о ее ненужности — смешными вам могут показаться самые неожиданные вещи.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[5]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
_>У вас просто великолепное понимание, особенно для незнакомого с декларативными языками. Только вот один важный вывод из ваших же мыслей вы упустили. Как вы правильно заметили, монады задают императивные конструкции в декларативных языках. Т.е. они являясь лишь небольшой частью языка, пытаются выполнить ту же задачу, для реализации которой предназначена вся мощь современных императивных языков. Т.е. грубо говоря монады являются убогонькой альтернативой самим императивным языкам. И соответственно использовать их здесь в явном виде довольно глупо (но совсем не сложно, как было показано в начале этой темки).
Монады не задают императивные конструкции в декларативных языках. Монады это отделение вычислений от побочных эффектов.
_>Да, кстати, поскольку монады задают не произвольный императивный код, а с весьма специфическими параметрами, то их можно изредка встретить в обычном императивном коде. Т.е. это не значит, что авторы планировали создать монаду. Они просто писали обычный императивный код под специфическую задачку и в результате у них нарисовалась типичная монада (им от этого правда ни холодно, ни жарко). Как примеры:
Из ложной посылки следует ложный вывод. Поскольку монады отделяют вычисления от побочных эффектов, то это можно использовать и в императивных языках.
_>Так что в принципе это вещь естественная для специфических задачек. А убогость соответственно заключается в том, что работая только с монадами, мы должны писать вообще весь императивный код только таким способом.
Снова неверно. Во первых, пиши как угодно, ну разве что в Хаскеле есть внятные ограничения. Во вторых, если хочешь отделять побочыные эффекты от вычислений, выбор у тебя на самом деле невелик.
Re[89]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>ОК. А где на этой диаграмме будут статические методы статического класса?
Формально то способ есть — отобразить как статические методы обычного класса и всё. Но на практике этот класс вообще не рисуют, т.к. он по сути является всего лишь заменой (и кстати весьма убогой) пространств имён, а функции в нём обычно носят утилитарный характер. Естественно мы говорим про ООП программу.
K>В C# есть статический класс. Что, C# тоже не совместим с UML, как и хаскель?
Ну во всяком случае в стандате UML никакого специального выделения для статических классов нет. Однако это естественно не мешает отображать их при надобности (хотя при классическом ООП это сомнительно) как обычный класс. А вот для генерации подобного кода видимо уже придётся использовать только фирменые инструменты от MS, в которых наверняка это как-то добавлено. )))
K>В хаскеле не бывает свободных функций, они всегда находятся, по крайней мере, в каком-то модуле. И если вы не опишете никакого модуля — они будут "публичными членами" модуля Main.
Даже если забыть про то, что мы так и не договорились по факту наличия модулей на диаграмме классов, то в любом случае здесь они являются всего лишь разновидностью пространства имён — никаких связей прорисовать не удастся. Т.е. получится не связанная диаграмма, некий набор независимых значков. Или может вы например подскажете, как нам например отобразить какие функции из данного модуля работают с каким-то из данного АТД (одного из определённых в том же модуле)?
K>Не нужно возвращается к массиву модулей, потому что нет связи между тем в чем что-то инкапсулируется и в чем что-то хранится. Да, классы в ООП совмещают несколько функций, которые в других языках могут не совмещаться в одном инструменте, но это не значит, что инкапсуляции в них нет — с чего вы этот разговор и начали.
Ага, есть инкапсуляция... Ну например:
1. Если у нас есть несколько АТД определённых в модуле и плюс имеем в модуле функции, работающие с приватными внутренностями этих АТД, то я правильно понимаю, что все функции внутри этого модуля имеют доступ ко внутренностям всех АТД, даже если реально не имеют к ним никакого отношения?
2. Я могу у какого-то АТД сделать доступ снаружи только к части данных? Т.е. не закрывая всё и вводя публичную функцию типа геттера, а именно доступ к самим данным, но частичный.
Это только то что с ходу в голову пришло, без изучения деталей. Подозреваю, что если начать копаться, то столько всего найти можно...
K>Собственно в том, что в классах можно что-то инкапсулировать и является проявлением того, что классы обладают некоторыми свойствами/функциями модулей. Поэтому, если про C можно точно сказать, что модулей в нем нет — то в случае C++ есть нюанс — потому что классы — это тоже, в общем-то, модули (схемеры бы сказали, наверное, "модули для бедных").
Вообще то для этих целей в C++ служат пространства имён, которые обладают своими мощными возможностям, уж точно превосходящими классы C++, а скорее всего и модули Хаскеля (тут вообще сложно сравнивать, т.к. предназначения немного различаются всё же — пространства имён никак не завязаны на компиляцию и т.п.)
K>Нет, мап самый обычный. Если вашу дишную чистоту нельзя использовать для того, чтоб функция могла проконтролировать, что в нее именно чистые функции передаются — то никаким надмножеством чистоты в математическом смысле она не является.
Ну естественно можно написать map, принимающий только чистые функции. Только нафига он мне такой-то, зачем себя ограничивать? Я вполне могу себе представить сценарии, в которых будет удобно применить map с не чистой функцией.
K>Видите ли, абстракция должна иметь какие-то свойства, иначе ее нельзя использовать без "протекания". В ФП, где абстракции используют нешуточным образом, это особенно очевидно и важно. K>В большинстве языков программирования, конечно, никаких удобств для ФП нет — ну, тем больше головной боли для программиста, который вдруг решит на них фп-код писать. K>Впрочем, позиция понятна, очередная колбаса, потребности в которой в вашем дишном коммунизме нет.
Я так и не понял какое отношение эта тирада имеет к вопросу о нужности требования чистоты от параметров map'a.
K>Каким образом это можно обеспечить, если "чистые" функции могут изменяемые данные получить?
Нуу грубо говоря:
class A {v=1;};
auto a=new A;
auto x=[a, a, a];//ссылки на один объектauto f(А p) pure {++p.v; return v;}
auto y=map!f(y);//после этого вызова имеем:
//x=[A{4}, A{4}, A{4}]
//y=[A{2}, A{3}, A{4}]
Т.е. хотя мы и меняем изначальный массив хитрым образом, то на результате это никак не отражается. И соответственно map p.q=map p . map q будет работать корректно, т.к. в map q мы будем менять не изначальный массив, а некий промежуточный.
K>Это фрагмент кода, из примера с изменяемым буфером. Который демонстрирует как раз то, о чем я говорил — устранение ненужных промежуточных структур. Код на ди, как я понял, так не оптимизируется, вы заменили конвейер на одну функцию find вручную. Разумеется все промежуточные данные в моем примере иммутабельные. Ну так о них и разговор.
Ну а я вот не уверен, что наличие мутабельных данных (даже не смотря на то, что в самом примере их никто не меняет) никак не влияет на получающийся код. На внешность то изначального кода это влияет и довольно заметно...
K>Когда мы с вами обсуждали — наоборот — поддержку мутабельности в хаскеле — я вам показывал устранение ненужных копирований при работе с мутабельными массивами.
Да, было такое. Но работало только благодаря искусственно введённой вами поддержке этого. В данном же примере такого не было вроде...
K>Ну правильно, если у вас персистентные структуры слишком тормозят, вы будете их использовать только в крайнем случае. Но код, который с персистентными структурами работает обычно понятнее и меньше ошибок содержит. Поэтому в языке с поддержкой иммутабельности такие структуры применяют просто для удобства программиста.
Да глупо это и не нормально по сути. Вот объясните мне зачем мне хранить историю изменения некой переменной, если я точно знаю, что старое значение мне больше никогда не понадобится (как в том же примере с обработкой кадров видео)?
K>Повторяю в очередной раз. Это один из двух важных аспектов. Другой — устранение промежуточных данных на этапе компиляции. Если есть и то и другое — иммутабельностью можно даже начинать пользоваться, а не обосновывать, почему без нее можно обойтись. K>Каждый раз, когда я говорю, что при работе с персистентными структурами будет выделятся куча объектов в памяти, в том числе и много короткоживущих, вы скипаете текст и в очередной раз удивляетесь — чего это я такое значение сборщику мусора придаю. Попробуйте связать одно с другим. Связь есть — точно вам говорю.
Ещё раз, я не говорю о том, что неважна скорость работы сборщика мусора (тем более, если речь о языке, в котором это единственный способ выделения памяти). Я пытаюсь напомнить, что кроме сборщика мусора может ещё множество разных вариантов. Может быть и банальный подсчёт ссылок (кстати, вот Apple выкатили язык вообще базирующий на этом), может быть аллокатор на пуле, может быть вообще ручная работа с указателями, а может быть и моё любимое выделение на стеке (кстати, для некоторых персистентных структур такое вполне может быть интересно, если у них сами данные хранятся единым куском в динамической памяти, а бегают туда сюда различные ссылки на него, которые вполне эффективно хранить на стеке), да и мало ли что можно придумать в языке с полным доступом на низкий уровень. И большинство из перечисленных мною вариантов будут работать быстрее любого сборщика мусора (из любого языка). Ну и соответственно если вдруг очень хочется пострадать иммутабельными структурами, то никто не мешает построить их и вокруг такого базиса.
Re[6]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Монады не задают императивные конструкции в декларативных языках. Монады это отделение вычислений от побочных эффектов.
Насколько я понимаю, ты согласен с тем, что монады встречаются не только в Хаскеле? Если да, то покажи ка мне на любом примере и любом языке, кроме Хаскеля, как там монады отделяют побочные эффекты.
I>Из ложной посылки следует ложный вывод. Поскольку монады отделяют вычисления от побочных эффектов, то это можно использовать и в императивных языках.
А я разве где-то написал, что их нельзя использовать? ) Вроде как наоборот, я даже привёл примеры того, где монады сами появляются (даже если авторы их и не задумывали) в императивном коде. Правда почему-то это всё очень специфические задачки... )))
I>Снова неверно. Во первых, пиши как угодно, ну разве что в Хаскеле есть внятные ограничения. Во вторых, если хочешь отделять побочыные эффекты от вычислений, выбор у тебя на самом деле невелик.
Ограничения у нас будут, если мы будем строить весь императивный код на монадах. Если же нет, то действительно никаких проблем нет. )
Re[7]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>Монады не задают императивные конструкции в декларативных языках. Монады это отделение вычислений от побочных эффектов.
Вообще, я немного погорячился. монады отделяют не просто побочные эффекты от вычислений, а эффекты, вообще, от вычислений.
_>Насколько я понимаю, ты согласен с тем, что монады встречаются не только в Хаскеле? Если да, то покажи ка мне на любом примере и любом языке, кроме Хаскеля, как там монады отделяют побочные эффекты.
I>>Из ложной посылки следует ложный вывод. Поскольку монады отделяют вычисления от побочных эффектов, то это можно использовать и в императивных языках.
_>А я разве где-то написал, что их нельзя использовать? ) Вроде как наоборот, я даже привёл примеры того, где монады сами появляются (даже если авторы их и не задумывали) в императивном коде. Правда почему-то это всё очень специфические задачки... )))
Давай перечислим вместе эти специфические задачи
обработка исключительных ситуаций.
вычисления с множественными результатами
асинхронный результат
обработка событий
вычисления с изменяемым состоянием
ввод-вывод
Как видишь, задачи мягко говоря широко распространены.
I>>Снова неверно. Во первых, пиши как угодно, ну разве что в Хаскеле есть внятные ограничения. Во вторых, если хочешь отделять побочыные эффекты от вычислений, выбор у тебя на самом деле невелик.
_>Ограничения у нас будут, если мы будем строить весь императивный код на монадах. Если же нет, то действительно никаких проблем нет. )
Ограничения появятся в том случае, если ты решишь отделить эффекты от вычислений. Точно так же, если ты решил хранить данные в SQL Server, то у тебя сразу соответсвующие ограничения связаные с этим решением.
Вот взять Promise — эффект здесь временная задержка, с помощью монады вычисления отделяются от эффекта, то есть, задержки.
Если ты захочешь, что бы код вычисления был в одном месте, то в императивном коде у тебя будут вагоны приседаний скажем с многопоточностью, синхронизацией потоков, марашлингом и блокирующими вызовами. Все это _очень_ недешево.
Re[90]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
K>>Повторяю в очередной раз. Это один из двух важных аспектов. Другой — устранение промежуточных данных на этапе компиляции. Если есть и то и другое — иммутабельностью можно даже начинать пользоваться, а не обосновывать, почему без нее можно обойтись. K>>Каждый раз, когда я говорю, что при работе с персистентными структурами будет выделятся куча объектов в памяти, в том числе и много короткоживущих, вы скипаете текст и в очередной раз удивляетесь — чего это я такое значение сборщику мусора придаю. Попробуйте связать одно с другим. Связь есть — точно вам говорю.
_>Ещё раз, я не говорю о том, что неважна скорость работы сборщика мусора (тем более, если речь о языке, в котором это единственный способ выделения памяти).
Klapaucius говорит не про скорость. точнее основной его поинт не в скорости работы GC. А ты рассматриваешь GC исключительно со стороны перформанса.
>Я пытаюсь напомнить, что кроме сборщика мусора может ещё множество разных вариантов. Может быть и банальный подсчёт ссылок
Ну вот этого достаточно. Каким это образом подсчет ссылок вдруг поможет с освобождением памяти, в которой вагон циклических ссылок, что собтсвенно типичный случай ?
То есть, в кратце, если говорить про автоматический сбор, то это только GC. Все остальные это просто варианты ручной работы. Скажем, с циклическими ссылками придется протащить методы в АПИ, только для того, что бы память корректно освобождалась.
Re[89]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Klapaucius, Вы писали:
K>Нет, мап самый обычный. Если вашу дишную чистоту нельзя использовать для того, чтоб функция могла проконтролировать, что в нее именно чистые функции передаются — то никаким надмножеством чистоты в математическом смысле она не является.
С помощью Dивной статической интроспекции проконтролировать, чтобы передаваемые функции были только действительно чистыми, совсем не сложно.
Они с проблемами недостаточно чистых ф-й согласны, но говорят, что если такие ф-и заворачивать в полностью чистые (с иммутабельными параметрами), то сразу получаешь как бенефиты хаскельной чистоты, так и более развязанные руки по реализации этих чистых ф-й внутри.
Re[86]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, alex_public, Вы писали:
I>>В свою очередь классика ФП разделяет эти вещи — типы отдельно от модулей. Что характерно, и там и там в основе типов лежит АТД. Отсюда ясно что оба подхода могут и используют один и тот же UML
_>Да, безусловно можно разработать некий вид диаграмм (и даже включить их в стандарт UML), которые будут отлично отображать эти две сущности. Могу даже предложить для этого взять за основу диаграмму классов, в которой чуть поменять свойства для значка класса, добавить новый значок для модулей, и соответственно пересмотреть набор возможных видов связей.
Не надо ничего менять. Во первых, в ООП и так есть модули, хотя бы на уровне библиотеки. Во вторых, если в Хаскеле класть руками один АТД в один модуль, то получится тот самый класс.
Более того, наследование в ООП используется
1. как уточнение типа
2. как расширение функционала как модуля
И то и другое требует вообще говоря разных стрелочек, но на практике обходятся одной. Более того, можно вообще все стрелочки заменить на стрелочка + её назначение, наприме 'use'. Собтсвенно именно это и видим.
_>Т.е. основная моя мысль в том, что в теории то без проблем, а на практике (в существующих стандартах и соответственно инструментах) фигово у чистых ФЯ с этим делом.
ФЯ всего лишь не имеют внятной поддержки на уровне синтаксиса, потому что все скобочки заюзали под АТД и прочие вещи, для ОО ничего не осталось. Остаётся поддерживать ОО на уровне библиотеки.
Re[8]: Есть ли вещи, которые вы прницпиально не понимаете...
Здравствуйте, Ikemefula, Вы писали:
I>Вообще, я немного погорячился. монады отделяют не просто побочные эффекты от вычислений, а эффекты, вообще, от вычислений.
Что такое в данном контексте эффекты, вычисления и что значит "отделяют"?
Re[8]: Есть ли вещи, которые вы прницпиально не понимаете...
Ничего не понял. Где тут монада, где вычисления, а где побочные эффекты?
I>Давай перечислим вместе эти специфические задачи I>обработка исключительных ситуаций. I>вычисления с множественными результатами I>асинхронный результат I>обработка событий I>вычисления с изменяемым состоянием I>ввод-вывод
I>Как видишь, задачи мягко говоря широко распространены.
Не не не.. Это ты по сути перечисляешь области, в которых используются инструменты построенные внутри с помощью монад (чаще всего не специально)... При таком раскладе проще было сказать вообще "всё программирование" и не ошибиться. Но это будет уже вторая итерация использования. А я то говорю о первой (о тех самых инструментах), а их как раз совсем не много.
Смотри, монада у нас получается если:
1. Есть некий контейнер, для разных типов.
2. На базе него мы строим конвейер последовательной обработки данных.
3. Конвейер строится из функций специфического вида: T1 -> C<T2>, где C — это наш контейнер.
4. Соответственно преобразование (условно говоря) вида C<T> -> T происходит автоматически на каждом шаге конвейера и как раз и задаёт ту самую полезную функциональность данной конкретной монады.
Всё это в сумме задаёт очень специфическую схему. Которая может удобно укладываться только на редкие задачки, типа той же реализации библиотеки продолжений или билиотеки диапазонов. А вот то, что эти библиотеки потом могут использоваться очень много где, никто и не спорит.
I>Вот взять Promise — эффект здесь временная задержка, с помощью монады вычисления отделяются от эффекта, то есть, задержки.
Ну скажем promise/future из C++11 (в которых нет then) не имеют никакого отношения к монадам. Но тем не менее точно так же абстрагируют результат от задержки. )
I>Если ты захочешь, что бы код вычисления был в одном месте, то в императивном коде у тебя будут вагоны приседаний скажем с многопоточностью, синхронизацией потоков, марашлингом и блокирующими вызовами. Все это _очень_ недешево.
Ты похоже пропустил, что я писал. В большинстве случаев при написание указанных выше библиотек никто не старался специально "построить монаду". Они просто писали обычный императивный код, создавая оптимальную архитектуру под свою конкретную задачу. И вот иногда при этом получаются схемы полностью попадающие под определение монады. Ну так и отлично. Или ты считаешь, что я против такого, и обнаружив внезапно возникшую в моём коде монаду, резко побегу менять всю архитектуру? )))
Бредовость я вижу в другом. Если человек начнёт специально стараться получать монады везде в своём коде — вот это уже клиника...