Re[58]: Мифический Haskell
От: vdimas Россия  
Дата: 09.04.12 22:47
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>У нас тут оверхед, потому что на деле нужен только один элемент из массива (остальные всё равно средствами самого Haskell не получить из-за свойств языка), но вот такова реализация. Зато память интерпретируется достоверно.


Это она где-то в середине операции интерпретируется достоверно. А до начала операции достоверно ничего неизвестно.
И да, это очередная эмуляция механики происходящего в Хаскель, поздравляю.

VE>Это я для простоты нарисовал массив, на деле-то можно сгенерировать структурку с типизированными указателями.


Это ничего не изменит, так же как в твоей реализации union на "типизированной" структуре их 2-х указателей. Все равно до проверки указателей неизвестно, по какому из них лежит целевое значение, т.е. неизвестно, как интерпретировать содержимое АлгТД-обертки.

VE>2. Давай реализуем АлгТД как: функция, принимающая кортеж обработчиков, и вызывающая ровно один из них при каждом case of.



VE>
VE>// Either a b
VE>typedef std::function<void (ptr)> onData;
VE>typedef std::function<void (onData, onData)> either;

VE>// foo e = case e of
VE>//    Left x -> expr1
VE>//    Right y -> expr2
VE>void foo(either const & e)
VE>{
VE>  e([] (ptr x) { expr1 }, [] (ptr y) { expr2 });
VE>}
VE>


VE>Теперь АДТ — функция.


А что поменялось? Кто-то должен будет сделать диспетчеризацию, т.е. выбрать саму ф-ию в любом случае.
Даже это было необязательно. Бо ты здесь ограничен примитивами С++, а компилятор может сделать простой jump/call по косвенной адресации. При нормальном кодировании метки типа ветвление можно сделать за O(1) на таблице независимо от числа вариантов. Здравствуй ООП-like полиморфизм.


VE>Что такое "интерпретация памяти"? Когда мы compile-time знаем, какого типа данные лежат в памяти? Ну вот знаем теперь, и что?


Если уйдут парные операции запаковки/распаковки — то то. Избавишься от лишних затрат, связанных с динамической распаковкой. Я кстате постоянно критикую увлечение подражательством этой технике на других языках — на дотнете/C#, например, где эту технику непосредственно вручную эмулируют. Мне возражают — это удобства ради. Т.е. это выходит такой способ группировки операций через объединение данных. ИМХО, почти всегда можно обойтись без группировки данных, проведя преобразования кода, которые ты предлагал. Ну, кроме случая зафиксированности таких данных во внешнем контракте, который будет открыт для реализации извне.


V>>Далее. Должен существовать оператор динамической проверки и приведения типа, где на входе этого оператора будет неуточненный тип выражения, а на выходе — уточненный. Заметь, приведение к объемлющему типу происходит "бесплатно": для С++ в худшем случае константное смещение указателя/ссылки, если база не первая в списке, а для Хаскеля — это безусловное конструирование АлгТД. Зато переход от объемлющего типа к уточненному требует или dynamic_cast в С++, или динамическое приведение в яве/дотнете, или ПМ в Хаскеле/Немерле. Т.е. первая операция выполняется за O(1), то вторая за O(n), где n — общее кол-во тестируемых уточненных типов.


VE>Т.е. ПМ в Haskell выполняется за O(n), да?


ХЗ. Мне недавно сказали, что в GHC кодирует не последовательные константы для юзверских АлгТД, а некую ссылку на описатель. Выходит, прямая табличная диспетчеризация недоступна при ветвлении по метке конструктора.

VE>Давайте разбираться.


VE>На вход мы даём выражение одного типа, на выходе (возможно) получаем другое выражение другого типа, так ещё и с другим адресом.

VE>Под эти свойства подходит даже взятие элемента массива или доступ к полю объекта. Или разыменование указателя.

И что? Ты ведь сам показал в своих преобразованиях, что почти всегда этого можно НЕ делать. Т.е. если решать задачу таким же образом как в Хаскеле — то да, придется эмулировать работу АлгТД, никто не спорит. Но ведь можно не складывать типы в мешок, можно ведь протаскивать не данные в некий контракт, а наоборот, применить DI и затребовать типизированный обработчик-визитор (твой кортеж типизированных ф-ий). Классика жанра, однако.

VE>Я тут пытался описать свойства такой функции.

VE>Так вот на мой взгляд эти свойства выглядит так:
VE>
VE>-- каст туда-обратно гарантированно возвращает то же выражение
VE>dynamicCast (cast x) = Just x
VE>-- если каст успешен, то исходное выражение было получено кастом в обратную сторону
VE>(dynamicCast e = Just v) -> (e = cast v)
VE>

VE>Если будешь дополнять, то лучше псевдокодом, а не словами.

Не буду. Я многократно соглашался, что от dynamic_cast убежать более чем легко. Ты мне предлагаешь убежать не через пересмотр всего решения, а через техники-заменители, которые ни разу не изменяют исходное решение. Смысл? Количество if-ов, как ты правильно заметил, в этом случае НЕ изменится. Т.е. то ли ты проверяемый признак загонишь в систему типов, то ли в поле объекта, но если ты не сумел сделать перетащить этот признак из данных в поведение, то кол-во телодвижений будет одинаковое, с точностью до несущественных деталей.


VE>Но вот беда, для моего случая как динамика подходит указатель. int мы можем преобразовать в int*, а обратно — если указатель не равен 0.


Для случая maybe аналогично. Можно поинтересоваться, ты хотел что-то показать этим, или просто вслух рассуждаешь?

VE>Хуже того, int мы можем преобразовать в std::pair<bool, std::pair<int, float> >, а обратно — second.first или second.second в зависимости от флага. Перечисленным выше свойствам это удовлетворяет.

VE>Я догадываюсь, какое свойство ты захочешь ввести, но попробуй его формализовать, и наверняка всплывут двойные трактовки. "Интуитивное" понимание меня волнует мало.

Зато я не догадываюсь, о чем догадываешься ты.
Поясни. Как системщик по образованию и призванию, я не делаю принципиальное различие м/у способами кодирования данных. Угу. Потому что при смене кодирования, пространство состояний (как минимум валидных состояний, т.е. интересующих нас), остается неизменно. Удачное кодирование данных может упростить схему, т.е. удешевить каждую/некоторую операцию в отдельности, но не изменить кол-во самих операций в координатах выбранного пространства состояний. Ты же пока упорно ставишь эксперименты в области кодирования этого пространства, доказывая (якобы доказывая), что кол-во операций при этом не изменится... Блин, но я это и так уже почти 20 лет прекрасно знаю. Вот что мне тебе на это отвечать? Давай уж тогда перейдем к вопросам оптимального кодирования состояний, коль оно тебя так волнует... будет чуть поинтересней... бо, скажем прямо, обсуждение maybe на указателях и прочий детсад малость потребляет терпение...


V>>Дык, а чего предлагать? В Хаскеле ты требуемый признак вложил в систему типов программы (Maybe), а в С++ этот признак искуственный, проверяется вручную.


VE>Правильно. Но какая разница, вручную или нет, это ведь останется динамической типизацией. То, что в Хаскеле закладывается в систему типов (Maybe, Either, any other ADT), в C++ придётся реализовать искусственно, а значит от "телодвижений в рантайм" никуда не деться.


Еще как деться. Ты же сам недавно показал неплохие преобразования. Кол-во операций ИЗМЕНЯЕТСЯ, потому что изменяется кол-во состояний. В твоем примере преобразования изчезало промежуточное состояние Either, т.е. изчезали все накладные расходы,связанные с его обслуживанием.


VE>По поводу реинтерпретации памяти. Мне не нравится это интуитивное понятие, потому что на самом нижнем уровне типов уже нет. У нас указатели и вызовы функций, принимающие указатели. Тот же Either a b можно представить как просто флаг и указатель void*.



Ты неисправим.
Ладно, завязываем.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.