Re[44]: Мифический Haskell
От: vdimas Россия  
Дата: 05.04.12 22:13
Оценка:
Здравствуйте, D. Mon, Вы писали:

DG>>значит можно утверждать, что тип String в Руби на самом деле является параметрическим типом String<TMemoryMapping>, который конкретизируется как:

DG>>String<MRI_18.String_Memory_Mapping>, String<IronRuby.String_Memory_Mapping>, String<JRuby.String_Memory_Mapping> в зависимости от интерпретатора

DM>Утверждать можно сколько угодно, толку то. Что это изменит и что хорошего даст? Пока что эти утверждения лишь вносят путаницу (разные способы параметризации типов) и противоречат спецификациям языков. Не первый раз ты уже смешиваешь систему типов языка и подробности реализации компиляторов/рантаймов.


Почему попытку разобраться в реализации всё время пытаются объявить "смешиванием"?
Это несерьезно и малость непрофессионально, ИМХО. Система типов — это не просто абстракция, это инструмент, предназначенный исключительно для реализации прикладных вещей. Т.е. чем больше функциональности мы перекладываем на систему типов, тем достовернее необходимо знать, во что это нам обходится. Иначе нет никакого смысла смысла использовать такую систему типов для реальных задач, коль ее характеристики неизвестны.
Re[46]: Мифический Haskell
От: vdimas Россия  
Дата: 05.04.12 22:26
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Сознание лишь информируется о решении задачи (и тогда мы восклицаем "Эврика!", но как именно получено решение — не понимаем) интеллектом.


Как, как... сопоставлением с образцом...
Образное мышление работает, а это штука слишком большой параллельности/размерности, чтобы объяснить последовательностью слов.

ИМХО, степень сознания человека на самом деле довольно низкая. Ниже, чем хотелось бы или чем многие пытаются приписать человеку. Человек еще слишком слабо себя познал, поэтому "сознание" в человеческом понимании — это банальная совокупность интеллекта + ощущений. Причем, и первое и второе не так чтобы очень сильны, скорее, наоборот. Просто человек слишком много о себе думает и вообще склонен к эзотерике. К 2050-му машины будут гарантированно умнее человека и смогут лучше "понимать" как происходит мыслительный процесс в человеке, чем сам человек. Останется добавить машинам ощущения для формулирования стимулов, как прямых так и производных сколь угодно больших уровней производности (типа как сегодня у человека "долг", "совесть" и прочих несложных стимулов низких уровней производных от стимула "собственная значимость") и идти на свалку истории.
Re[47]: Мифический Haskell
От: VoidEx  
Дата: 05.04.12 22:41
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Теперь представим, что у нас по этому дереву будет сотня независимых операций обработки, но выбор лишь из одной и это выбор зависит от данных в рантайм. Причем, выбор этот пусть по неким причинам происходит уже ПОСЛЕ построения дерева.


Ну, давай. Теперь реализуй это на Си++ и расскажи, куда там денется динамическая типизация.
Или никуда не денется?

VE>>Правда для общего случая. Разница в том, что общий случай с АлгТД имеет фиксированное число вариантов, в случае обычного ООП общий случай шире.


V>Нет, он непросто шире, он ОТКРЫТ, этот общий случай. Просто был бы шире — это фигня и сводится к варианту №1 или №2 из показанного в начале поста.


Мы как глухой со слепым разговариваем. Шире и означает, что он шире качественно, а не количеством вариантов.

V>Не, ты всерьез считаешь, что если в switch из десятка вариантов добавить ветку default, то что-то принципиально изменится? Я уже второго человека спрашиваю — тишина.


Это ты так считаешь. Одни if-else у тебя динамическая типизация, а другие — нет. Почему — чёрт его разберёт. Какая-то "реинтерпретация памяти" приплетается.

V>Т.е. от смены координат ничего ведь не изменяется и не должно. Просто для ООП мы рассуждаем об открытости иерархии, а для ФП — об открытости мн-ва доступных ф-ий.


Что мешает определить новую функцию в ООП?

V>Здесь ты уже потерял нить обсуждения, т.к. в предложенном тобой преобразовании мы парную упаковку/распаковку АлгТД заменяли на непосредственную обработку данных. А для этого надо заведомо "видеть" все случаи, где этот АлгТД обрабатывается. Я же уже одергивал тебя насчет своеобразной системы модулей Хаскеля, которые на самом деле не модули нихрена, а некий аналог #include из С/С++, с той разницей, что хранится уже распаршенное представление кода. Но этот код не черный ящик ни разу. Интеллектуальную собственность в него не спрячешь, поэтому коммерческих библиотек на Хаскеле нет и не будет. Ну разве что в виде распространяемых в исходниках на честном слове.


Зачем их видеть? Получать указатели на продолжения.

VE>>Вот в dotnet — это частный случай. А в Haskell — нет.


V>Т.е. на Хаскель нельзя написать интероперабельную с основной программой на Хаскеле DLL?


Что?

VE>>Аргументы будут?


VE>>В общем, без какого-либо осмысленного контраргумента дальше обсуждать тему бессымсленно.


V>В общем, пора бы взглянуть на реально происходящее и на свои собственные предложения со стороны. Твоя настойчивость насчет отказаться от св-в системы типов и эмулировать другую на основе имеющейся попахивает общим непониманием происходящего, сорри. Ты НЕ понимаешь, что система типов — это уже готовый инструмент, который тебе дан, чтобы ты НЕ писал свой, точно такой же. Пользуйся этим инструментом. Все что я писал здесь — это не критика инструмента, это раскрытие механики его работы и сравнение с механикой динамической типизации в других языках. Я показал, что эта механика абсолютно идентична, как бы тебе не больно было слышать это про "статически типизированный Хаскель".


Нет, это ты не понимаешь, что в случае, когда на Haskell есть т.н. тобой динамическая типизация, она есть и в Си++. При этом в Си++ это может выглядеть как банальный if-else. Даже если там сравнение x с нулем.

V>И ты это так и НЕ опровергнул, хотя, повторюсь, придумал несколько способов как избавиться от встроенной динамической типизации, но не так и НЕ понял, что избавляешься каждый раз через LSP, либо через эмуляцию динамической типизации...


Я утверждаю ровно одно: если от ветвления избавиться нельзя, до либо это динамическая типизация во всех таких случаях (и пофиг, сравнивается там x с нулем или проверяется метка типа), либо нет, и тоже во всех.

Именно поэтому я привожу код в сравнении с Си++ (в котором, на твой взгляд, в тех примерах, что я привожу, динамической типизации почему-то нет, хотя кол-во проверок то же), ты же упорно съезжаешь на обсуждение только хаскельного кода, пытаясь объяснить мне очевидное, даже признавая, что преобразования идентичны. Так я именно на эту идентичность и упираю.
Re[55]: Мифический Haskell
От: VoidEx  
Дата: 05.04.12 22:54
Оценка:
Здравствуйте, vdimas, Вы писали:

Давай с чистого листа, а то надоело уже.
Дай формальное определение динамической (статической) типизации.

Статичность типизации — возможность гарантированно убрать ветвление.

Т.е. если в обоих языках по 2 ветвления при одинаковом поведении, то их статичность равномощна.

При моём определении две программы ниже статически типизированы в одинаковой мере. Однако третья — более статически типизирована:
И наплевать, что в Си++ просто ветвление, а в Haskell — ADT.
Предложи своё определение.

// int * foo();
int * x = foo(10);
if (x)
  std::cout << *x << std::endl;
else
  std::cout << "null" << std::endl;


-- foo :: IO (Maybe Int)
x <- foo 10;
case x of
  Just v -> print v
  Nothing -> putStrLn "null"


-- возвращает пруф, что если n - чётно, то результат just
-- foo : (`n : int) -> io (`r : maybe int) {even `n -> is-just `r = true}
x <- foo 10
case x of
  just v -> print v
  -- nothing -> never happens
Re[45]: Мифический Haskell
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 06.04.12 03:31
Оценка: +2
Здравствуйте, vdimas, Вы писали:

V>Почему попытку разобраться в реализации всё время пытаются объявить "смешиванием"?


Я согласен, что представлять, во что превращаются данные в памяти, нередко полезно и иногда даже необходимо. Но это просто знание подробностей компилятора. Хорошее, полезное знание. Просто не надо эту информацию толкать обратно в язык и говорить, что "на самом деле" там типы такие и такие. Нет, "на самом деле" компиляторов может быть много, и определение языка от них не должно зависеть. Если вбить себе в голову, что такой-то тип представлен именно так, а не иначе, это может привести к проблемам, когда оптимизатор вдруг возьмет и сделает по-другому. Это даже в Си ежедневно случается, что уж говорить о более сложных языках.
Re[43]: Мифический Haskell
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 06.04.12 05:28
Оценка: +1
Здравствуйте, DarkGray, Вы писали:

DG>или другими словами:

DG>int<TMemoryMapping>

Проблема в том, что этот TMemoryMapping настолько изменчив, что практически не дает никакой полезной информации. Даже в рамках одного компилятора один и тот же тип int может быть отображен на железо по-разному. Простой вопрос: как представлена переменная i в следующем коде?
int a[4];
for(int i=0; i<4; i++)
  a[i] = 111;

Она может оказаться на стеке, в регистре, или вообще ее может не быть после инлайнинга. Будешь включать этот нюанс в определение типа int?
Re[55]: Мифический Haskell
От: mima  
Дата: 06.04.12 05:53
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Ага, а чем отличается код первокурсника от кода опытного программиста? ИМХО, помимо прочего еще тем, что первокурсник всё пишет ручками, а опытный программист умеет заставить работать вместо себя систему типов. Типы-то там нужны для того, чтобы переносить на них часть прикладной логики, правильно? А ты в этом примере решил побыть тем самым первокурсником, не сумевшим расписать иерархию в ООП или АлгТД в ФП? И написал ручками свой if?


Точно-точно! http://www.willamette.edu/~fruehr/haskell/evolution.html

В ваших программах есть if?

И ещё, вы часто пишите об "уточнённых типах". Под ними вы подразумеваете конструкторы данных АлгТД? Или типы полей этих конструкторов?

Вот тут, например, что является уточнёнными типами?
data T = A A1 A2 ...
       | B B1 B2 ...

f t = case t of
  A a1 a2 ... => foo a1 a2 ...
  B b1 b2 ... => bar b1 b2 ...
Re[44]: Мифический Haskell
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 06.04.12 13:09
Оценка: +1
DG>>или другими словами:
DG>>int<TMemoryMapping>

DM>Проблема в том, что этот TMemoryMapping настолько изменчив, что практически не дает никакой полезной информации.


только надо быть честным и формулировать правильно: TMemoryMapping настолько изменчив, там настолько много информации, что я, D.Mon не знаю, как с этим можно работать.

фактически, когда ты говоришь что здесь нет полезной информации, ты на самом деле говоришь, что здесь нет неизменной полезной информации, а как сделать большой объем изменяющейся информации — полезным для себя, ты не знаешь.

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

DM> Даже в рамках одного компилятора один и тот же тип int может быть отображен на железо по-разному. Простой вопрос: как представлена переменная i в следующем коде?

DM>
DM>int a[4];
DM>for(int i=0; i<4; i++)
DM>  a[i] = 111;
DM>

DM>Она может оказаться на стеке, в регистре, или вообще ее может не быть после инлайнинга. Будешь включать этот нюанс в определение типа int?

если необходимо управлять эффективностью этого кода, то да — это необходимо добавить.
и оптимизируеший компилятор когда генерить код, именно этим и занимается, он каждую переменную конкретизирует местом ее хранения (heap, регистр, stack, и т.д.), при условии, конечно, что переменная не была элиминирована в результате оптимизации

соответственно задача звучит так:
хотим ли мы чтобы такие нюансы описывались всегда неявно автоматически компилятором и при этом получался не очень эффективный код,
или хотим чтобы можно было описывать такие нюансы, как явно руками в критических точках, так и неявно автоматически компилятором в остальном коде и получать при этом максимально эффективный код
?

если этот массив часто используется в критичном с точки зрения производительности кода, то данный код может быть вообще размечен как:
array<int<structured>, size:4, dll-data> a = compile-run([1..4].Select(_=>111).ToArray());

выполнить генерацию последовательности 111, 111, 111, 111 на этапе компиляции и поместить в сегмент данных скомпилированного модуля

если размечать данных код без особых оптимизаций под простой набор команд процессора, то будет:
array<stack, int<structured>, size:4> a;
for(int<register> i=(int<code>)0; i < (int<register>)(int<code>)4; i++)
{
   a[i] = (int<code>)111;
}

*structured — часть некой большей структуры. Вопросы выделения памяти, освобождения памяти и т.д. решает большая структура.
*code — число хранится напрямую в коде, как часть ассемблерной команды load x

этот же код может быть размечен так (при развертывании for-а на этапе компиляции):
array<stack, int<structured>, size:4> a;
for<compile>(int<compile> i=0; i < 4; i++)
{
   a[i] = (int<code>)111;
}

и т.д.
Re[45]: Мифический Haskell
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 06.04.12 16:29
Оценка: +1
Здравствуйте, DarkGray, Вы писали:

DG>>>int<TMemoryMapping>


DM>>Проблема в том, что этот TMemoryMapping настолько изменчив, что практически не дает никакой полезной информации.


DG>только надо быть честным и формулировать правильно: TMemoryMapping настолько изменчив, там настолько много информации, что я, D.Mon не знаю, как с этим можно работать.


Нет, я о другом. Если включать TMemoryMapping в систему типов языка, то никакой вообще информации там не будет, т.к. значения его не будут известны. Ты не можешь заранее знать и включить в систему типов все подробности всех будущих реализаций компиляторов и других способов исполнения. Программу может вообще не компьютер исполнять, а группа бразильских студенток. Как у них в головах представлен int? Внесешь это в систему типов?

Когда у тебя есть конкретный компилятор, ты действительно можешь сказать, что он в таких-то ситуациях представляет этот тип таким-то образом. Но это информация об этом конкретном компиляторе, а не об исходном языке. Поэтому вносить TMemoryMapping в систему типов нет смысла. В код конкретного компилятора — да, смысл есть и вполне понятный.
Re[46]: Мифический Haskell
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 06.04.12 23:45
Оценка:
DM>Нет, я о другом. Если включать TMemoryMapping в систему типов языка, то никакой вообще информации там не будет, т.к. значения его не будут известны. Ты не можешь заранее знать и включить в систему типов все подробности всех будущих реализаций компиляторов и других способов исполнения. Программу может вообще не компьютер исполнять, а группа бразильских студенток. Как у них в головах представлен int? Внесешь это в систему типов?

в этом тексте смешивается сразу несколько тем:
уровни абстрагирования кода,
должен ли язык быть один для разных уровней абстрагирования или это должны быть разные языки?
должна ли система типов быть одной, разной или единой?

При программировании может делаться много разных уровней абстрагирования разными способами, сейчас остановимся на трех:
1. пока мы описываем математические вычисления как абстрактный алгоритм — нам достаточно типа Number (и на этом уровне не конкретизуется, целое ли это число, комплексное или еще какое-то, как оно хранятся в памяти, каким образом выглядят ошибки вычисления и т.д.)

2. дальше этот алгоритм применяется в какой-то конкретной задаче: здесь мы уже скорее всего знаем, каким число будет: целым, дробным, какие ошибки при вычислении могут быть и как мы их хотим показывать пользователю

3. еще дальше мы эту конкретную задачу хотим выполнить на конкретной платформе: здесь нас начинает интересовать размерность чисел, способ хранения в памяти, какие типы и операции процессор подерживает нативно, скорость выполнения каждой операции и т.д.

Нельзя считать, что задача программиста заканчивается на 2 уровне, или тем более только на первом.
В задачу программиста входит правильно сформировать код на всех трех уровнях. Да, эта задача с одной стороны громоздкая, а с другой стороны — повторяющаяся, и поэтому в ее решении может помочь "машина", но именно помочь, а не заменить программиста полностью. При этом роли распределяются следующим образом: программист принимает ключевые решения, а "машина" на основе этой ключевой информации выстраивает промежуточные связки.

Язык должен быть один для работы со всеми уровнями. 2 + 2 должно записываться одинаковым образом: и при записи общей математической формулы, и при работе на уровне ассемблера. Тоже самое касается логических операций, опрераций с множествами, последовательностями и т.д. Это необходимо чтобы легко было перейти от уровня, а так же смешать в одной программе несколько уровней.

Система типов должна быть разнообразной, но при этом единой. С одной стороны — на каждом уровне свои типы: на первом — number, sequence, collection, set и т.д., на втором — float, integer, array, dictionary и т.д., на третьем — native-int, byte, float32 и т.д., с другой стороны — и программист, и "машина" должны понимать как number переходит integer, а затем в native-int32, и что number=integer<TMemoryMapping, TRange> | float<TMemoryMapping, TRange, TAccuracy> | complex<..>, а x86-native-int32 = integer<signed, size:4, little-endian>


DM>Когда у тебя есть конкретный компилятор, ты действительно можешь сказать, что он в таких-то ситуациях представляет этот тип таким-то образом. Но это информация об этом конкретном компиляторе, а не об исходном языке. Поэтому вносить TMemoryMapping в систему типов нет смысла. В код конкретного компилятора — да, смысл есть и вполне понятный.


Если TMemoryMapping отдается полностью на откуп компилятору, то у программиста пропадает возможность управлять эффективностью выполнения своего кода, не смотря на то, что именно программист намного лучше компилятора знает в каких точках программы ему нужна максимальная производительность, и как этого можно добиться.

Хуже всего, что присутствует самообман. Одной рукой программист для одних данных задает низкоуровневое управление памятью, а другой рукой закрывает себе глаза и говорит, что отказывается смотреть как другие данные будут себя вести на нижнем уровне.
Например, конструкция:
data Tree a = Leaf | Node a (Tree a) (Tree a)

явно задает, что дерево хранится, как ссылочное бинарное дерево.
и т.к. в коде уже всё на это завязано, то, в целом, уже невозможно это дерево хранить в виде упорядоченного массива, которое тоже является бинарным деревом.

на первом уровне абстракции достаточно сказать, что в данном месте мы работает с последовательностью, коллекцией, множеством и т.д..
на втором уровне абстракции уже хочется поговорить о том, какие операции над коллекцией(последовательностью и т.д.) должны делаться быстрее всего,
и только на самом последнем уровне — может идти речь о том, что это будет: ссылочное бинарное дерево, двунаправленный список, отсортированный массив или hashset.
Re[48]: Мифический Haskell
От: vdimas Россия  
Дата: 07.04.12 02:21
Оценка:
Здравствуйте, VoidEx, Вы писали:

V>>Теперь представим, что у нас по этому дереву будет сотня независимых операций обработки, но выбор лишь из одной и это выбор зависит от данных в рантайм. Причем, выбор этот пусть по неким причинам происходит уже ПОСЛЕ построения дерева.


VE>Ну, давай. Теперь реализуй это на Си++ и расскажи, куда там денется динамическая типизация.

VE>Или никуда не денется?

По крайней мере итерация по типам узлов из стоимости O(n) на ПМ превращается в O(1) на двойной диспетчеризации (визитор/мультиметоды), в случае итерации по описанному дереву ПРОИЗВОЛЬНОГО кол-ва алгоритмов.



V>>Не, ты всерьез считаешь, что если в switch из десятка вариантов добавить ветку default, то что-то принципиально изменится? Я уже второго человека спрашиваю — тишина.

VE>Это ты так считаешь. Одни if-else у тебя динамическая типизация, а другие — нет.

Дык, если этот if-else идет по результату предиката проверки типа, то динамическая ес-но. Не люблю повторяться, но напомню, что речь о двухтактной схеме: проверка -> реинтерпретация памяти.


VE>Почему — чёрт его разберёт. Какая-то "реинтерпретация памяти" приплетается.


Неужели "туман" еще не рассеялся?


V>>Т.е. от смены координат ничего ведь не изменяется и не должно. Просто для ООП мы рассуждаем об открытости иерархии, а для ФП — об открытости мн-ва доступных ф-ий.

VE>Что мешает определить новую функцию в ООП?

Да ничего. Но это не повлияет на твою технику подстановки кода в АлгТД, т.к. объект — это не АлгТД.



V>>Здесь ты уже потерял нить обсуждения, т.к. в предложенном тобой преобразовании мы парную упаковку/распаковку АлгТД заменяли на непосредственную обработку данных. А для этого надо заведомо "видеть" все случаи, где этот АлгТД обрабатывается. Я же уже одергивал тебя насчет своеобразной системы модулей Хаскеля, которые на самом деле не модули нихрена, а некий аналог #include из С/С++, с той разницей, что хранится уже распаршенное представление кода. Но этот код не черный ящик ни разу. Интеллектуальную собственность в него не спрячешь, поэтому коммерческих библиотек на Хаскеле нет и не будет. Ну разве что в виде распространяемых в исходниках на честном слове.


VE>Зачем их видеть? Получать указатели на продолжения.


Не получится. Согласно приведенной тобой технике, тебе надо уметь отследить жизненный путь любого ЭКЗЕМЛЯРА такого АлгТД, создание которого заменяется сразу на обрабатывающий его код. Ты просто порисуй граф преобразований при вложенности более 2-х, увидишь, насколько все интересно... Особенно интересно будет, когда в программе будет несколько независимых АлгТД, но которые "унутре" состоят из пересекающегося мн-ва других типов. После такого "раскрытия скобок" у нас группировка по АлгТД пропадет, но появится новая, по этим типам, входящим в первоначальные АлгТД. И еще могут быть заметны устойчивые повторяющиеся группы ф-ий даже для разных типов. Т.е. произойдет своебразная перегруппировка: первоначальные группы (АлгТД) распадутся, и станут заметны новые группы, схожие не по структуре, а по наборам операций. Кароч, это будет преобразование от декомпозии на АлгТД к классической ООП-декомпозиции.

Да, динамическая типизация по первоначальным типам изчезает, ты абсолютно правильно заметил. Но она исчезает потому, что исчезают исходные типы — в это вся суть фокуса.


V>>Т.е. на Хаскель нельзя написать интероперабельную с основной программой на Хаскеле DLL?

VE>Что?

Можно ли на Хаскель написать DLL, которая содержит пересекающееся мн-во типов с основной програмой, тоже написанной на Хаскеле? И при этом взаимодействует с участием типов из этой области пересечения?


VE>Нет, это ты не понимаешь, что в случае, когда на Haskell есть т.н. тобой динамическая типизация, она есть и в Си++.


Не факт. Она будет только если мы на С++ выберем такую же декомпозицию по типам и ролям этих типов. Просто в ООП скорее всего будет чуть другая декомпозия, и многие вещи будут решены через ООП-полиморфизм, что есть O(1) затрат для преобразования this к произвольному наследнику при вызове ф-ий.


VE>При этом в Си++ это может выглядеть как банальный if-else. Даже если там сравнение x с нулем.


Или как вызов виртуальной ф-ии. А в особо клинических случаях — двойная или тройная диспетчеризация.


VE>Я утверждаю ровно одно: если от ветвления избавиться нельзя, до либо это динамическая типизация во всех таких случаях (и пофиг, сравнивается там x с нулем или проверяется метка типа), либо нет, и тоже во всех.


Таки курить LSP до полного просветления. Как раз способ избавиться от цепочки if-else, проверяющей метки типов. Кстати, в Хаскеле тоже доступно, только слабо используется...
Re[56]: Мифический Haskell
От: vdimas Россия  
Дата: 07.04.12 02:32
Оценка:
Здравствуйте, mima, Вы писали:

V>>Ага, а чем отличается код первокурсника от кода опытного программиста? ИМХО, помимо прочего еще тем, что первокурсник всё пишет ручками, а опытный программист умеет заставить работать вместо себя систему типов. Типы-то там нужны для того, чтобы переносить на них часть прикладной логики, правильно? А ты в этом примере решил побыть тем самым первокурсником, не сумевшим расписать иерархию в ООП или АлгТД в ФП? И написал ручками свой if?


M>Точно-точно! http://www.willamette.edu/~fruehr/haskell/evolution.html


Ооо, этот баян растет вширь.


M>В ваших программах есть if?


Судя по виденному мною Хаскель-коду — на порядок меньше... Стараюсь обходится полиморфизмом или простой +1 косвенностью вместо опроса признаков. if — зло для современных конвейеров процессоров. Циклы/рекурсии на каждый чих — тоже.


M>И ещё, вы часто пишите об "уточнённых типах". Под ними вы подразумеваете конструкторы данных АлгТД? Или типы полей этих конструкторов?


Согласно определению, это "другой тип внутри АлгТД"


M>Вот тут, например, что является уточнёнными типами?

M>[haskell]
M>data T = A A1 A2 ...
M> | B B1 B2 ...

Tuple(A1, A2) и Tuple(B1, B2).
Re[56]: Мифический Haskell
От: vdimas Россия  
Дата: 07.04.12 04:12
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Давай с чистого листа, а то надоело уже.

VE>Дай формальное определение динамической (статической) типизации.

Есть еще строгая/нестрогая типизация.

Если своими словами, то так: статическая строгая типизация — это достоверная интерпретация памяти, занимаемой значениями типов, известная в compile-time. Динамическая строгая типизация — это типизация, которая требует телодвижений в рантайм, т.е. когда невозможно достоверно интерпретировать память, занимаемую этим значением, до проверки типа.

Итого, фактически весь мейнстрим статически-типизированных языков содержит возможности динамическоой проверки и приведения типов. От классичесских динамических скриптовых языков статически-тииированные из мейнстрима отличаются только тем, что не располагают инструментом утиной типизации. Т.е. вызов ф-ий (методов) всегда будет статически-типизирован. Это позволяет не хранить имена ф-ий в рантайм.

В общем, если язык статически-типизируемый, то переменная не может быть неопределенного типа, т.е. если на классическое определения динамической типизации наложим ограничения статической, то это должен быть такой тип, известный в compile-time, что переменной такого типа можно присвоить значение другого типа, то бишь согласно логике групп, который может объединить в себе другие типы, к которым переменная такого типа может быть динамически приведена. Для ООП примером может быть некий базовый класс и его наследники. Для Хаскеля — АлгТД.

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


VE>Статичность типизации — возможность гарантированно убрать ветвление.

VE>Т.е. если в обоих языках по 2 ветвления при одинаковом поведении, то их статичность равномощна.

Простое ветвление на if, это если n=2. А если больше, то больше.
Если правильно закодировать метки типов, то ветвление по меткам можно заменить на табличную диспетчеризацию, и тогда вместо O(n) получим O(1). На это трюке сделан ООП-полиморфизм в мейнстриме, который позволяет перейти от объемлющего типа к уточненному за O(1). Поэтому у меня в коде очень мало if. Я уже писал рядом, что if — это зло.



VE>При моём определении две программы ниже статически типизированы в одинаковой мере. Однако третья — более статически типизирована:

VE>И наплевать, что в Си++ просто ветвление, а в Haskell — ADT.
VE>Предложи своё определение.

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

На С++ можно так же вложить в систему типов, на статическом полиморфизме, если код разруливается на шаблонах или на динамическом, если не получается.
Re[47]: Мифический Haskell
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 07.04.12 08:32
Оценка:
Здравствуйте, DarkGray, Вы писали:

DG>При программировании может делаться много разных уровней абстрагирования разными способами, сейчас остановимся на трех:

DG>1. пока мы описываем математические вычисления как абстрактный алгоритм — нам достаточно типа Number (и на этом уровне не конкретизуется, целое ли это число, комплексное или еще какое-то, как оно хранятся в памяти, каким образом выглядят ошибки вычисления и т.д.)

DG>2. дальше этот алгоритм применяется в какой-то конкретной задаче: здесь мы уже скорее всего знаем, каким число будет: целым, дробным, какие ошибки при вычислении могут быть и как мы их хотим показывать пользователю


DG>3. еще дальше мы эту конкретную задачу хотим выполнить на конкретной платформе: здесь нас начинает интересовать размерность чисел, способ хранения в памяти, какие типы и операции процессор подерживает нативно, скорость выполнения каждой операции и т.д.


Разумно. Хотя третий пункт далеко не всегда наступает, миллионы программистов на похапе и питоне успешно решают свои задачи, не имея представления о об этом третьем уровне. Что уж говорить о тех, кто пишет на JavaScript'e.

DG>Нельзя считать, что задача программиста заканчивается на 2 уровне, или тем более только на первом.


Таки можно очень часто (JS называют самым популярным ЯП, а там третьего уровня нет вообще). Но не всегда, согласен.

DG>Язык должен быть один для работы со всеми уровнями.


Не должен. Таких языков сейчас вообще около двух штук всего.

DG>Система типов должна быть разнообразной, но при этом единой. С одной стороны — на каждом уровне свои типы: на первом — number, sequence, collection, set и т.д., на втором — float, integer, array, dictionary и т.д., на третьем — native-int, byte, float32 и т.д., с другой стороны — и программист, и "машина" должны понимать как number переходит integer, а затем в native-int32, и что number=integer<TMemoryMapping, TRange> | float<TMemoryMapping, TRange, TAccuracy> | complex<..>, а x86-native-int32 = integer<signed, size:4, little-endian>


Я выше уже сказал, почему это невозможно. Потому что система типов фиксируется, а подробности отображения данных на железо все время новые появляются. Без libastral не обойтись. Ты уже решил, как отразить в системе типов устройство бразильских студенток?

DM>>Когда у тебя есть конкретный компилятор, ты действительно можешь сказать, что он в таких-то ситуациях представляет этот тип таким-то образом. Но это информация об этом конкретном компиляторе, а не об исходном языке. Поэтому вносить TMemoryMapping в систему типов нет смысла. В код конкретного компилятора — да, смысл есть и вполне понятный.


DG>Если TMemoryMapping отдается полностью на откуп компилятору, то у программиста пропадает возможность управлять эффективностью выполнения своего кода,


Это не совсем так. Если программист знает, как именно данный компилятор маппит данные на память, он может принимать решения и пытаться управлять эффективностью. Именно это делают сейчас С/С++ программисты. Никто не мешает кроме информации о языке использовать информацию о компиляторе. Просто я за то, чтобы не смешивать их в одном ведре. Более того, компилятор может позволить программисту управлять этим маппингом через опции компилятора. Так это и делается сейчас. И это именно опции компилятора, а не языка.

DG>Хуже всего, что присутствует самообман. Одной рукой программист для одних данных задает низкоуровневое управление памятью, а другой рукой закрывает себе глаза и говорит, что отказывается смотреть как другие данные будут себя вести на нижнем уровне.

DG>Например, конструкция:
DG>
DG>data Tree a = Leaf | Node a (Tree a) (Tree a)
DG>

DG>явно задает, что дерево хранится, как ссылочное бинарное дерево.

Это у тебя самообман. Данная конструкция ничего такого не задает. Значение типа Tree a может быть отлично представлено функцией, принимающей пару других функций. Почитай про STG-машину, использовавшуюся в GHC, например.
Re[48]: Мифический Haskell
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 07.04.12 12:10
Оценка:
DM>Разумно. Хотя третий пункт далеко не всегда наступает, миллионы программистов на похапе и питоне успешно решают свои задачи, не имея представления о об этом третьем уровне. Что уж говорить о тех, кто пишет на JavaScript'e.

всё смешиваешь в кучу.
если бы их не интересовала производительность, то зачем для этих языков пишутся оптимизирующие компиляторы, mozilla в javascript вводит типизированные массивы и т.д.?

речь идет о том, что эти люди ради производительности не готовы переходить на C++, но в тоже время они готовы оптимизировать свои решения, если бы они при этом остались в рамках парадигмы своего языка, и увеличение производительности не требовало бы от них сильное увеличение доли ручного труда.
имхо, редкий программист отказался бы от wizard-а, который бы говорил, что:
функция zz написана неоптимально — переписать?
функция zz2 написано тоже неоптимально, но автоматически переписать не получается. Перепишите ее, пожалуйста, сами, или давайте перепишем совместно.

DG>>Нельзя считать, что задача программиста заканчивается на 2 уровне, или тем более только на первом.


DM>Таки можно очень часто (JS называют самым популярным ЯП, а там третьего уровня нет вообще). Но не всегда, согласен.


это дилетантский подход человека, который далек от js.

вот, например, 3-ий уровень в js:
https://developer.mozilla.org/en/JavaScript_typed_arrays

в js лимитирующим фактором является связь с сервером, которая обычно составляет 30-100мс, соответственно, пока код на js маленький и выполняется меньше, чем за 100мс, то смысла его оптимизировать особого нет.
но с каждым годом всё больше переносится функционала на js, он всё больше тормозит, и возникает задача его оптимизировать.

DG>>Язык должен быть один для работы со всеми уровнями.


DM>Не должен. Таких языков сейчас вообще около двух штук всего.


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

DM>Я выше уже сказал, почему это невозможно. Потому что система типов фиксируется, а подробности отображения данных на железо все время новые появляются. Без libastral не обойтись. Ты уже решил, как отразить в системе типов устройство бразильских студенток?


и в чем ты видешь проблему?
мне, кажется, что ты считаешь, что система типов — это должно быть что-то статичное, один раз и навсегда заданное, а это не так.
система типов — это динамическая штука, которая перестраивается и вычисляется каждый раз заново при каждом изменении кода.
например, если в систему типов введены pre/post-condition-ы, то каждое изменение кода — эти condition-ы перестраивает.

> Ты уже решил, как отразить в системе типов устройство бразильских студенток?


они используют какое-то своё представление чисел, коллекций и т.д.?
для того, чтобы конкретизировать код для выполнения группой бразильских студенток, достаточно описать устройство этой группы:
какие типы для них являются нативными, какие операции они умеют делать, сколько времени каждая операция занимает, какая вероятность ошибочного выполнения каждой операции.
скорее всего выяснится, что коллекции малого объема и числа с низкой точностью являются нативными типами, остальное поддерживается плохо. значит придется ввести типы: маленькая коллекция/большая коллекция и конкретизировать код с учетом этих типов.

DG>>Если TMemoryMapping отдается полностью на откуп компилятору, то у программиста пропадает возможность управлять эффективностью выполнения своего кода,


DM>Это не совсем так. Если программист знает, как именно данный компилятор маппит данные на память, он может принимать решения и пытаться управлять эффективностью. Именно это делают сейчас С/С++ программисты. Никто не мешает кроме информации о языке использовать информацию о компиляторе. Просто я за то, чтобы не смешивать их в одном ведре.


не надо себя обманывать, то что пишется в исходнике является частью языка, а никак иначе

и почти все реальные программы на C/C++ вовсю используют явное управление памятью.
например, с помощью следующих pragm:
http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

DM> Более того, компилятор может позволить программисту управлять этим маппингом через опции компилятора. Так это и делается сейчас. И это именно опции компилятора, а не языка.


наивное представление.
исходники программы — это десятки МБ информации
опции компилятора — это максимум 10 байт информации.
из теории информации следует, что 10 байт информации никогда не сможет управлять маппингом 10МБ информации

DM> Просто я за то, чтобы не смешивать их в одном ведре.


плохо формулируешь.

fill(collection, val)=foreach(ref item in collection)item=val;

fill<x86>(collection<лежащая-одним-куском-в-памяти, item=byte>, item<byte> val)={ecx:=collection.len;eax:=val;rep stosb;}


вот это смешивание? или это все-таки записанное в одном языке правильное явное разделение кода на два уровня абстрагирования, и явное задание, как данная функция должна конкретизироваться при заданной конкретике.

зы
вообще это напоминает маленький детей, которые считают, что если они не будет смотреть на страшное, то страшное не будет смотреть на них.
почему ты считаешь, что если ты не будешь смотреть на то, как компилятор конкретизирует твой код, он от этого сразу станет конкретизироваться правильно и эффективно?

DG>>Хуже всего, что присутствует самообман. Одной рукой программист для одних данных задает низкоуровневое управление памятью, а другой рукой закрывает себе глаза и говорит, что отказывается смотреть как другие данные будут себя вести на нижнем уровне.

DG>>Например, конструкция:
DG>>
DG>>data Tree a = Leaf | Node a (Tree a) (Tree a)
DG>>

DG>>явно задает, что дерево хранится, как ссылочное бинарное дерево.

DM>Это у тебя самообман. Данная конструкция ничего такого не задает. Значение типа Tree a может быть отлично представлено функцией, принимающей пару других функций. Почитай про STG-машину, использовавшуюся в GHC, например.


приведи программу, в которой функции принимают такое представление дерева, а при конкретизации программы данное дерево мапится на массив
Re[49]: Мифический Haskell
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 07.04.12 16:09
Оценка: +2
Здравствуйте, DarkGray, Вы писали:

DM>>Разумно. Хотя третий пункт далеко не всегда наступает, миллионы программистов на похапе и питоне успешно решают свои задачи, не имея представления о об этом третьем уровне. Что уж говорить о тех, кто пишет на JavaScript'e.


DG>всё смешиваешь в кучу.


Наоборот, пытаюсь твою кучу разделить на отдельные слои.

DG>если бы их не интересовала производительность, то зачем для этих языков пишутся оптимизирующие компиляторы, mozilla в javascript вводит типизированные массивы и т.д.?


Оптимизирующие компиляторы пишутся как раз людьми, знакомыми с устройством компилятора и рантайма (работающими на третьем уровне абстракции), для людей, которые в эти дебри не опускаются, работая на первых двух. Людьми, пишущими на одном языке (обычно С/С++) для пишущих на другом (похапе, питон, JS...). От того, что какой-нибудь Ларс Бак написал быстрый движок JS, средний JS-программист Коля ничего не узнал о хранении целых чисел в памяти интерпретатора. На разных уровнях разные люди и разные языки. А ты тут смешиваешь все эти языки и уровни в одну кашу.

DM>>Таки можно очень часто (JS называют самым популярным ЯП, а там третьего уровня нет вообще).


DG>это дилетантский подход человека, который далек от js.

DG>вот, например, 3-ий уровень в js:
DG>https://developer.mozilla.org/en/JavaScript_typed_arrays

Я действительно далек от JS. Но скажи, пожалуйста, входят ли эти массивы в стандарт языка и не являются ли частной оптимизацией конкретного интерпретатора. Можно ли их использовать в IE и Chrome, например. Если нет, то это вряд ли часть языка.

DG>>>Язык должен быть один для работы со всеми уровнями.

DM>>Не должен. Таких языков сейчас вообще около двух штук всего.
DG>логически не правильно построенная фраза. уровень "как оно есть сейчас", ни коим образом не пересекается с уровнем "как оно должно быть", и соответственно фактами с уровня "как оно есть" нельзя опровергнуть "как оно должно быть".

У тебя "должен" значит "ДаркГрей хочет", а у меня "должен иметь свойство" значит "обязательно имеет", так это слово понимают в математике. Если в ряде успешных языков некоторого свойства нет, значит язык не обязан его иметь, не должен. Но может. И да, кому-то это может быть желательно, но это не называется "должен".

DG>>>Если TMemoryMapping отдается полностью на откуп компилятору, то у программиста пропадает возможность управлять эффективностью выполнения своего кода,


DM>>Это не совсем так. Если программист знает, как именно данный компилятор маппит данные на память, он может принимать решения и пытаться управлять эффективностью. Именно это делают сейчас С/С++ программисты. Никто не мешает кроме информации о языке использовать информацию о компиляторе. Просто я за то, чтобы не смешивать их в одном ведре.


DG>не надо себя обманывать, то что пишется в исходнике является частью языка, а никак иначе


Да, конкретно в случае С/С++ это часть языка. Но я бы не стал обобщать опыт С++ на все остальные языки.

DM>> Более того, компилятор может позволить программисту управлять этим маппингом через опции компилятора. Так это и делается сейчас. И это именно опции компилятора, а не языка.

DG>наивное представление.
DG>исходники программы — это десятки МБ информации
DG>опции компилятора — это максимум 10 байт информации.
DG>из теории информации следует, что 10 байт информации никогда не сможет управлять маппингом 10МБ информации

Ну да, конечно. Включение/выключение поддержки исключений задается простым ключом компилятора и очень сильно влияет на компиляцию всего кода, хоть 10МБ, хоть 10000МБ. Аналогично с определением wchar_t и пр. Размер исходников программы вообще не имеет значения.

DM>> Просто я за то, чтобы не смешивать их в одном ведре.


DG>вообще это напоминает маленький детей, которые считают, что если они не будет смотреть на страшное, то страшное не будет смотреть на них.

DG>почему ты считаешь, что если ты не будешь смотреть на то, как компилятор конкретизирует твой код, он от этого сразу станет конкретизироваться правильно и эффективно?

С чего ты взял, что я так считаю? Я так не считаю.
А почему ты считаешь, что Y-комбинатор все хотят писать на ассемблере?

DG>>>Например, конструкция:

DG>>>data Tree a = Leaf | Node a (Tree a) (Tree a)
DG>>>явно задает, что дерево хранится, как ссылочное бинарное дерево.

DM>>Это у тебя самообман. Данная конструкция ничего такого не задает. Значение типа Tree a может быть отлично представлено функцией, принимающей пару других функций. Почитай про STG-машину, использовавшуюся в GHC, например.


DG>приведи программу, в которой функции принимают такое представление дерева, а при конкретизации программы данное дерево мапится на массив


Зачем? И что такое конкретизация программы, кто и когда ее производит?
Re[49]: Мифический Haskell
От: VoidEx  
Дата: 07.04.12 16:33
Оценка:
Здравствуйте, vdimas, Вы писали:

V>По крайней мере итерация по типам узлов из стоимости O(n) на ПМ превращается в O(1) на двойной диспетчеризации (визитор/мультиметоды), в случае итерации по описанному дереву ПРОИЗВОЛЬНОГО кол-ва алгоритмов.


Какая такая итерация в O(n)? Давай-ка псевдокод.

V>Дык, если этот if-else идет по результату предиката проверки типа, то динамическая ес-но. Не люблю повторяться, но напомню, что речь о двухтактной схеме: проверка -> реинтерпретация памяти.


define реинтерпретация памяти.

V>Неужели "туман" еще не рассеялся?


Сейчас ты дашь определение, и окажется, что оно крайне туманно и зависит от конкретной реализации, а не от системы типов.

VE>>Зачем их видеть? Получать указатели на продолжения.


V>Не получится. Согласно приведенной тобой технике, тебе надо уметь отследить жизненный путь любого ЭКЗЕМЛЯРА такого АлгТД, создание которого заменяется сразу на обрабатывающий его код.


Что? Согласно моей технике вместо АлгТД передаётся тупл функций.

V>Можно ли на Хаскель написать DLL, которая содержит пересекающееся мн-во типов с основной програмой, тоже написанной на Хаскеле? И при этом взаимодействует с участием типов из этой области пересечения?


Говоря проще, можно ли из DLL на Haskell передать в программу, например, Maybe Int?
Re[50]: Мифический Haskell
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 07.04.12 17:05
Оценка: :)
DM>Оптимизирующие компиляторы пишутся как раз людьми, знакомыми с устройством компилятора и рантайма (работающими на третьем уровне абстракции), для людей, которые в эти дебри не опускаются, работая на первых двух. Людьми, пишущими на одном языке (обычно С/С++) для пишущих на другом (похапе, питон, JS...). От того, что какой-нибудь Ларс Бак написал быстрый движок JS, средний JS-программист Коля ничего не узнал о хранении целых чисел в памяти интерпретатора. На разных уровнях разные люди и разные языки. А ты тут смешиваешь все эти языки и уровни в одну кашу.

Результат разработчика оптимизирующего компилятора Ларса Бака можно представить в виде белого открытого ящика доступного из ЯП.
Тогда бы программист Коля мог бы скачать библиотечку от D.Mon-а, вставить ее в JS, и трансформировать видео прямо в браузере.
Пока же исполнение JS остается черным закрытым ящиком — это невозможно.

DM>Я действительно далек от JS. Но скажи, пожалуйста, входят ли эти массивы в стандарт языка и не являются ли частной оптимизацией конкретного интерпретатора. Можно ли их использовать в IE и Chrome, например. Если нет, то это вряд ли часть языка.


в chrome входит, в IE — хз. и это означает, что код для FF и chrome будет работать быстро, а IE — медленно, и доля IE сократится еще в два раза.
и пофигу что там говорит стандарт.

DM>У тебя "должен" значит "ДаркГрей хочет", а у меня "должен иметь свойство" значит "обязательно имеет", так это слово понимают в математике. Если в ряде успешных языков некоторого свойства нет, значит язык не обязан его иметь, не должен. Но может. И да, кому-то это может быть желательно, но это не называется "должен".


в математике нет слова должен.
а вот в RFC есть в виде MUST, и означает, что zz должен иметь yy, чтобы соответствовать xx.
и я утверждаю, что для того, чтобы сгенеренный код для произвольной программы соответствовал обеспечению максимальной производительности, ЯП должен быть один для всех уровней.

DM>Да, конкретно в случае С/С++ это часть языка. Но я бы не стал обобщать опыт С++ на все остальные языки.


и в каких промышленных языках это не так?

DG>>из теории информации следует, что 10 байт информации никогда не сможет управлять маппингом 10МБ информации


DM>Ну да, конечно. Включение/выключение поддержки исключений задается простым ключом компилятора и очень сильно влияет на компиляцию всего кода, хоть 10МБ, хоть 10000МБ. Аналогично с определением wchar_t и пр. Размер исходников программы вообще не имеет значения.


не путай влиять и управлять.
управление генерацией кода имеет в виду, что для максимальной эффективности — каждая функция из 10МБ исходников должна быть собрана со своими настройками. что не может быть закодировано с помощью 10 байт информации.


DM>А почему ты считаешь, что Y-комбинатор все хотят писать на ассемблере?


а где я говорил, что ты хочешь писать его на ассемблере?

DM>>>Это у тебя самообман. Данная конструкция ничего такого не задает. Значение типа Tree a может быть отлично представлено функцией, принимающей пару других функций. Почитай про STG-машину, использовавшуюся в GHC, например.


DG>>приведи программу, в которой функции принимают такое представление дерева, а при конкретизации программы данное дерево мапится на массив


DM>Зачем?


ты выдвинул определенный тезис. подверди его.

DM> И что такое конкретизация программы, кто и когда ее производит?


конкретизация — придание более точного вида чему-либо.
частный случай конкретизации — задание параметров при параметрическом полиморфизме.
конкретизацией занимается явно программист, если язык поддерживает параметрический полиморфизм.
конкретизация может делаться программистом не явно.
большую работу по конкретизации выполняет компилятор, конкретизируется, например, какие переменные должны быть регистрами, какие участками в памяти, какие должны элиминироваться; конкретизируется как типы кладутся на память; конкретизуется каким ассемблерным инструктам будет соответствовать каждая строчка кода и т.д.
Re[57]: Мифический Haskell
От: VoidEx  
Дата: 07.04.12 17:45
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Если своими словами, то так: статическая строгая типизация — это достоверная интерпретация памяти, занимаемой значениями типов, известная в compile-time. Динамическая строгая типизация — это типизация, которая требует телодвижений в рантайм, т.е. когда невозможно достоверно интерпретировать память, занимаемую этим значением, до проверки типа.


Очень размыто, но допустим.

1. Давай реализуем АлгТД как: индекс конструктора + массив указателей на данные. При доступе мы просто берём указатель с нужным индексом. Т.е.
struct ADT
{
  int i;
  ptr ctor[N];
};

ptr getADT(ADT & x) { return x.ctor[x.i]; }


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

2. Давай реализуем АлгТД как: функция, принимающая кортеж обработчиков, и вызывающая ровно один из них при каждом case of.
// Either a b
typedef std::function<void (ptr)> onData;
typedef std::function<void (onData, onData)> either;

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


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

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

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


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

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

На вход мы даём выражение одного типа, на выходе (возможно) получаем другое выражение другого типа, так ещё и с другим адресом.
Под эти свойства подходит даже взятие элемента массива или доступ к полю объекта. Или разыменование указателя.
Я тут пытался описать свойства такой функции.
Так вот на мой взгляд эти свойства выглядит так:
-- каст туда-обратно гарантированно возвращает то же выражение
dynamicCast (cast x) = Just x
-- если каст успешен, то исходное выражение было получено кастом в обратную сторону
(dynamicCast e = Just v) -> (e = cast v)

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

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

VE>>Статичность типизации — возможность гарантированно убрать ветвление.

VE>>Т.е. если в обоих языках по 2 ветвления при одинаковом поведении, то их статичность равномощна.

VE>>При моём определении две программы ниже статически типизированы в одинаковой мере. Однако третья — более статически типизирована:

VE>>И наплевать, что в Си++ просто ветвление, а в Haskell — ADT.
VE>>Предложи своё определение.

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


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

// int * foo();
int * x = foo(10);
if (x)
  std::cout << *x << std::endl;
else
  std::cout << "null" << std::endl;


connection_data cdata;
if (load_config(&cdata))
{
  some_data sdata;
  int error_code = try_receive(&sdata);
  if (error_code == 0)
  {
    std::cout << "received " << sdata.bytes << " bytes" << std::endl;
    process(sdata.value);
  }
  else
  {
    std::cout << "error " << error_code << std::endl;
  }
}
else
  std::cout << "not loaded" << std::endl;


По моему определению — здесь в обоих случаях есть динамическая типизация в той же мере, что и в Haskell (в моём определении нет бинарного понятия, есть мера), но в меньшей, чем было бы на JavaScript, например.

V>На С++ можно так же вложить в систему типов, на статическом полиморфизме, если код разруливается на шаблонах или на динамическом, если не получается.


По поводу реинтерпретации памяти. Мне не нравится это интуитивное понятие, потому что на самом нижнем уровне типов уже нет. У нас указатели и вызовы функций, принимающие указатели. Тот же Either a b можно представить как просто флаг и указатель void*. А в зависимости от флага мы вызываем ту или иную функцию, которая принимает нетипизированный указатель. И вот в самом конце, когда наконец данные по указателю будут, например, выводить, произойдёт не реинтерпретация, а просто интепретация, при том ничего не стоящая. И получается, что динамичность типизации зависит от того, как будет проинтерпретирована память когда-то там намного позже. Причём принципиальной разницы между
if (flag)
  fooInt(ptr);
else
  fooFloat(ptr);

и
if (flag)
  fooInt1(ptr);
else
  fooInt2(ptr);

нет вообще.
Re[51]: Мифический Haskell
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 07.04.12 18:19
Оценка: +1
Здравствуйте, DarkGray, Вы писали:

DG>Результат разработчика оптимизирующего компилятора Ларса Бака можно представить в виде белого открытого ящика доступного из ЯП.


Так ведь ящик доступен не из ЯП (в данном случае JS), а из определенного интерпретатора — V8. В другом интерпретаторе в том же самом языке эти достижения уже недоступны. Поэтому я и говорю, что это не часть языка.

Кажется, ты все время срываешься с обсуждения ЯП в целом и существующих ЯП на обсуждение гипотетического языка твоей мечты, эдакого мегаС++. Который должен уметь то и предоставлять это.

DM>>Я действительно далек от JS. Но скажи, пожалуйста, входят ли эти массивы в стандарт языка и не являются ли частной оптимизацией конкретного интерпретатора. Можно ли их использовать в IE и Chrome, например. Если нет, то это вряд ли часть языка.


DG>в chrome входит, в IE — хз. и это означает, что код для FF и chrome будет работать быстро, а IE — медленно, и доля IE сократится еще в два раза.

DG>и пофигу что там говорит стандарт.

А мне пофиг на долю, мы говорим о теории ЯП и систем типов, а не о рынке браузеров. Нет в спецификации языка — значит нет в языке.

DM>>У тебя "должен" значит "ДаркГрей хочет", а у меня "должен иметь свойство" значит "обязательно имеет", так это слово понимают в математике. Если в ряде успешных языков некоторого свойства нет, значит язык не обязан его иметь, не должен. Но может. И да, кому-то это может быть желательно, но это не называется "должен".


DG>в математике нет слова должен.

DG>а вот в RFC есть в виде MUST, и означает, что zz должен иметь yy, чтобы соответствовать xx.

Хорошо, вот RFC-шную трактовку и возьмем, она достаточно строгая.

DM>>Да, конкретно в случае С/С++ это часть языка. Но я бы не стал обобщать опыт С++ на все остальные языки.

DG>и в каких промышленных языках это не так?

Похапе, JS, вероятно Java. Только зачем приплетать промышленность в разговор о теории ЯП?

DG>не путай влиять и управлять.

DG>управление генерацией кода имеет в виду, что для максимальной эффективности — каждая функция из 10МБ исходников должна быть собрана со своими настройками.

Ты только что сузил спектр обсуждаемых языков до ассемблера. Ни в каком другом языке ты не можешь всем этим управлять с нужной точностью.

DM>>А почему ты считаешь, что Y-комбинатор все хотят писать на ассемблере?

DG>а где я говорил, что ты хочешь писать его на ассемблере?

Нигде. Это я тебя пародирую, задаю вопрос, включающий ложную посылку.

DG>>>приведи программу, в которой функции принимают такое представление дерева, а при конкретизации программы данное дерево мапится на массив

DM>>Зачем?
DG>ты выдвинул определенный тезис. подверди его.

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