ЕЗЫГ МОЙ. Брейн дамп номер 2. Про типы с вариантами и простой matching

Начнём с примера. Есть дерево, в листьях хранятся строки, в ветках хранится длинна строк; короткие строки «слева», длинные «справа». В качестве примера будем брать функцию поиска строки в таком дереве.

Реализация на езыге

[code lang="cpp"]
// это тип с вариантом
switch Tree
{
case NULL:;

case Leaf:
struct
{
const char * itValue;
};

case Node:
struct
{
size_t length;
Tree * left;
Tree * right;
};
};

// а это matching для разных параметров
switch bool Find ( Tree * root, const char * text )
{
case bool Find ( Tree::Leaf * leaf, const char * text ):
{
return strcmp(leaf->itValue,text)==0;
};

case bool Find ( Tree::Node * node, const char * text ):
{
if ( strlen(text)<=node->length )
return Find(node->lessequal,text);
else
return Find(node->greater,text);
};

default:
{
return false;
};
};
[/code]

А, собственно, зачем всё это надо?

За таким паттерном тянется целый ряд идей. Matching это очень сильный механизм абстракции (например в Haskell http://www.haskell.org/tutorial/patterns.html ). Причём можно match-ить не только типы, но и константы или даже выражения.

[code lang="cpp"]
enum Operation
{
Add
, Sub
, Div
, Mul
};

// шаблонный matching с константой
switch int Exec ( int left, Operation op, int right )
{
case int Exec ( int left, Add, int right ): { return left + right; };
case int Exec ( int left, Sub, int right ): { return left - right; };
case int Exec ( int left, Mul, int right ): { return left * right; };
case int Exec ( int left, Div, int right ): { return left / right; };
case int Exec ( int left, Div, 0 ): { throw DIVISION_BY_ZERO; return 0; };
};
[/code]

Тип с вариантами позволяет жестко требовать соблюдение контрактов. Т-е matching не только должен перекрывать все варианты, но и быть type-safe. На практике это означает, что неперекрытие всех вариантов – ошибка компиляции, а не выполнения. Не надо ставить assert-ы, не надо писать unit-test-ы – такие баги просто не скомпилируются.

Matching во многом похож на существующие С++ шаблоны; основное отличие состоит в том, что часть решений принимается в runtime. При наличии RTTI шаблоны можно тоже перенести в runtime, получатся как-бы generics 

Типы с вариантами тоже могут быть шаблонными

[code lang="cpp"]
// шаблонный класс с вариантами
template
switch Tree
{
case NULL:;

case Leaf:
struct
{
DataType itValue;
};

case Node:
struct
{
KeyType itKey;
Tree * left;
Tree * right;
};
};
[/code]

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

[code lang="cpp"]
// наивная реализация
enum TreeType
{
TreeTypeNull = 0
, TreeTypeLeaf = 1
, TreeTypeNode = 2
};

struct Tree
{
TreeType itType;
};

struct TreeLeaf : public Tree
{
const char * itValue;
};

struct TreeNode : public Tree
{
size_t length;
Tree * lessEqual;
Tree * greater;
};

bool TreeFind ( Tree * root, const char * text )
{
TreeType nodeType = root ? root->itType : TreeTypeNull;
switch ( nodeType )
{
case TreeTypeLeaf:
{
TreeLeaf * leaf = (TreeLeaf *) root;
return strcmp(leaf->itValue,text)==0;
}
case TreeTypeNode:
{
TreeNode * node = (TreeNode *) root;
if ( strlen(text)<=node->length )
return TreeFind ( node->lessEqual, text );
else
return TreeFind ( node->greater, text );
}
default:
return false;
}
}
[/code]

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

[code lang="cpp"]
// чуть менее наивная реализация
struct VTree
{
};

struct VTreeLeaf : VTree
{
const char * itValue;
};

struct VTreeNode : VTree
{
size_t length;
VTree * lessEqual;
VTree * greater;
};

VTree * VTreeGetPtr ( VTree * ptr )
{
return (VTree *) ( size_t(ptr) & ~7 );
}

TreeType VTreeGetType ( VTree * ptr )
{
return (TreeType) ( size_t(ptr) & 7 );
}

bool VTreeFind ( VTree * root, const char * text )
{
TreeType nodeType = VTreeGetType(root);
switch ( nodeType )
{
case TreeTypeLeaf:
{
TreeLeaf * leaf = (TreeLeaf *) VTreeGetPtr(root);
return strcmp(leaf->itValue,text)==0;
}
case TreeTypeNode:
{
TreeNode * node = (TreeNode *) VTreeGetPtr(root);
if ( strlen(text)<=node->length )
return TreeFind ( node->lessEqual, text );
else
return TreeFind ( node->greater, text );
}
default:
return false;
}
}
[/code]

Оптимизаций сразу две. Данные не только занимают меньше места (нет больше переменной itType), но и ресолвятся быстрее. Лишний AND для ресолва указателя нужен не на всех платформах; даже там где он нужен, он легко окупается лёгкостью определения типа – зная тип данные нужны не всегда.

Возникает забавный сайд-эффект – expr.typeof это не метод класса, а метод указателя на класс. Т-е чтобы такие оптимизации были возможны, типы надо ресолвить на уровне указателей.

  • http://plakhov.livejournal.com Finder

    Да-да-да! Я как раз хотел написать про pattern matching и variant types вот после этого: http://plakhov.livejournal.com/51596.html.

    Два соображения:
    1. А точно ли нужен default? По-моему, это unsafe (при добавлении нового варианта, что бывает часто, можно забыть поправить какой-то matching, а компилятор не предупредит). Меня как раз расстраивает, что в С++ нет никакого “запретительного” аналога switch’а, обязующего программиста пробежать все варианты (например, по enum’у). Был бы – можно было бы variant types написать на темплейтах с макросами, я бы даже в таком виде ими пользовался.
    2. А еще если в языке есть variant types, то надо по умолчанию запрещать указателям быть нулевыми.
    То есть нельзя написать CWeapon *p = 0, но можно написать typedef (CWeapon*|None) PWeapon; PWeapon p = None; И работать с PWeapon через matching (если None – одна ветка, если CWeapon* – другая). Тогда у нас в compile time будут ловиться потенциальные null pointer exceptions.

  • Deemetrius

    А что делать, если типов к примеру 50? Это 6 бит на тип. А в указате всего 32 (64).
    В Езыге есть возможность добавлять пользовательские типы, так что их может быть неограниченное кол-во.

  • look4awhile

    думаю так-

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

  • look4awhile

    если 50 типов, то можно откатываться на наивную реализацию
    а можно глядеть, сколько будет стоить форсирование 64-байтного алайнмента
    а можно глядеть, нет ли нужного алайнмента уже
    как-то – так

  • http://plakhov.livejournal.com Finder

    Еще. Ты точно хочешь оставить template’ы? Я не уверен, что они вообще нужны при наличии variant types, скорее их можно заменить на анонимные variant’ы типа такого: (CCharacter*|CBuilding*|None) и на идею, что для них бывают match’и такого рода:

    void registerInVisibleList( CCharacter* pC );
    void registerInVisibleList( CBuilding* pB );

    switch void lookAt( (CCharacter*|CBuilding*|CSomething*) object )
    {
    case void lookAt( CSomething* object )
    { … }
    case void lookAt( (CCharacter*|CBuilding*) object )
    {
    registerInVisibleList( object );

    }
    }

  • http://plakhov.livejournal.com Finder

    Кстати, не уверен, что твой синтаксис switch-функций так уж хорош.
    Тут имхо больше подойдет идея, что switch-функция – это safe template.

    То есть синтаксис функций вообще остается как в С, а выражение Tree* root = …; callMethod( root ) означает вот что: если определены методы callMethod, покрывающие все варианты ровно по разу, то в этом месте делается неявный switch. Если же overload’ами callMethod покрыты не все варианты, случается compile time error. Получается естественная и милая замена сразу и виртуальным методам, и темплейтам, при этом довольно естественно продолжающая текущие идеи overloading’а функций по типу аргумента.

  • look4awhile

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

    шаблоны? тем более не уверен.
    склоняюсь к тому что будет matching и closures разных видов.

    + макросы на уровне ast – в идеале на том-же языке. OCaml такое неплохо делает.

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

  • http://plakhov.livejournal.com Finder

    Ну я собственно даже не за синтаксис ратую, а за следующую идею: не должно быть отдельно темплейтов, отдельно overload’ов, отдельно виртуальных методов (и несуществующие мультиметоды туда же), и отдельно variant types. Все это в геймдеве по сути используется одним и тем же образом, и все use case’ы лучше всего строятся как раз на базе variant type’ов.

    > мои собственные драфты в виде sexpr, только наружу их так выложить нельзя
    Как так нельзя? Здесь, по-моему, тусуется народ, к этому более чем готовый. :)

  • look4awhile

    ну давай попробуем

    обычный

    [code lang="lisp"]
    (variant tree ()
    (class leaf
    (string itValue))
    (class node
    (int length)
    (tree lessequal)
    (tree greater))
    (nil))
    [/code]

    “шаблонный”

    [code lang="lisp"]
    (macro make-tree ((type itType) (type keyType))
    (variant tree ()
    (class leaf
    (itType itValue))
    (class node
    (keyType length)
    (tree lessequal)
    (tree greater))
    (nil)))
    [/code]

  • look4awhile

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

  • http://plakhov.livejournal.com Finder

    Угу. Собственно моя идея такая: variant types – это first-class types. Второго варианта нет вообще, а вместо него объявление, аналогичное первому, на месте string и int – анонимные variant types c альтернативами: деревья чего, собственно, у тебя есть. То есть если у тебя s-выражения и AST макросы – то тебе второй вариант бесплатен, конечно, но вот если нету, то лучше совсем без него.

    Идея вместо темплейтного контейнера перечислить все его возможные варианты реализации может показаться дикой. Сначала. Но вот я поискал в нашем коде использования тех или иных контейнеров, и их для любого из них не больше, чем 5-10. О прочих использованиях шаблонов я вообще молчу, число вариантов обычно 2-3.

    Ну, кроме fixed capacity vector’а, конечно, который по-моему должен быть встроен в геймдев язык :)

  • look4awhile

    расширять тяжело. т-е неочевидно как написать, например, функцию – которая будет работать с классом, неизвестным на момент написания функции.

    контракты надо энфорсить, но явным перечислением страшно.

  • http://plakhov.livejournal.com Finder

    > как написать, например, функцию – которая будет работать с классом, неизвестным на момент написания функции

    Ну идея в том, что никак. Если тебе надо, чтобы функция стала работать с новым классом – поправь функцию.
    Почему, собственно, перечислять страшно? В plain old С, например, никто этого не пугался. Такое положение дел, правда, запрещает писать на ЕЗЫКЕ темплейтные библиотеки a la STL, и вообще код, который будет использоваться кем-то где-то как-то потом. Ну так оно и к лучшему.

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

  • look4awhile

    буду подумать. оно шибко против “скорость итерации” идет.

  • vshabanov

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

    Смотреть тут:
    http://caml.inria.fr/pub/docs/oreilly-book/html/book-ora115.html
    и, возможно, тут:
    http://caml.inria.fr/pub/docs/manual-ocaml/manual032.html

    Вкратце: варианты без параметров (тупые enum-ы) записываются как целые unboxed-числа. Целые числа на один бит меньше платформенного слова, т.к. младший установленный бит отличает число от указателя.
    Варианты с параметрами (да и все не immediate значения) содержат за одно слово перед указателем header (размером в слово), в котором обитает тег (8 бит, равен порядковому номеру среди вариантов или одной из констант, означающей, что это за блок) и информация, необходимая для сборщика мусора (2 бита — цвет, остальное — размер блока). Собственно параметры варианта (или переменные структуры) последовательно хранятся по данному указателю в виде таких же слов/указателей. А когда упираемся в примитивный тип, то по указателю уже хранятся данные.

    Вот собственно и все. Все значения имеют одинаковую структуру. Оверхед на не int и не enum всегда одно слово (хотя компилер много чего unbox-ит), и это уже со всеми данными для GC. И еще за счет одинаковой структуры имеется тру полиморфизм — не надо инстансить код темплейта для каждого типа, один и тот же полиморфный код работает со всеми типами.

  • look4awhile

    линки отличные. читать за OCaml вообще очень правильно – он живой вполне себе. и многим радует.

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

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

    header в куче случаев тоже не впился. полноценный сборщик мусора нужен только там где есть циклы в графе зависимостей, а такое далеко не всегда. хранить тип в варианте – увеличивать размер данных. вообщем реализация “в общем виде” – она хуже, чем приведённая.

    честный полиморфизм в OCaml не бесплатный – код получается ощутимо медленнее за счёт универсальности.

  • vshabanov

    Вычислительный оверхед — а много ли выполняется сложных расчетов с целыми числами. К тому же оверхида почти нет, ибо где надо все считается напрямую (компилер не тупой все таки), а установить/снести битик — не так уж сложно.

    Насчет хедера и полноценного GC: длина хедера — одно слово. Т.е. на ref count понадобится столько же.

    Никакого типа в варианте не хранится, у окемла вообще нет никакой RTTI. Ибо нафиг она нужна при статической типизации.

    Про ощутимо медленнее: на числодробильных задачах — да. Сишные компилеры очень хорошо заточены под них. А вот на логических задачах, где все равно идет проверка миллиона условий, и все SIMD с кешами идут по боку, окемл ничуть не уступает С. А за счет того, что сборщик мусора еще и поджимает данные ближе друг к другу, да и вообще пашет быстрее чем malloc/free кемл может и обогнать среднюю плюсовую прогу (реально наблюдалось).

  • look4awhile

    c целыми? измеримо вообщем-то. в своё время банальное обнуление всех переменных (для детерминизма) добавляло 2% оверхеда на игре, где я работал. пойнт в том, что лучше без чем с.

    часто бывает что не нужен refcount. и вообще ничего не нужно. любой leaf без варианта – прямой кандидат на не иметь никакого оверхеда вообще. тип в варианте в хедере вообщем-то.

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

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

  • http://vshabanov-ru.blogspot.com/ vshabanov

    …добавляло 2% оверхеда

    Ужас. Откуда такое кол-во неинициализированные переменных, да еще, по ходу, в логике.

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

    Про тип звиняйте, перепутал с типом, который в языке. Хотя 8 бит нифига не много. Да и не очень понимаю боязнь оверхеда, когда основные объемы все равно в арте живут. Пытаться сократить 10% памяти до 9%, как-то странно.

    Спуститься уровнем ниже можно практически отовсюду. Приведенные линки собсно и есть мануалы по дайвингу )

    Вообще, сочинять свой язык — это самое последнее дело. Это конечно очень интересно, но нифига не оправдано. Фишка в том, что существующие на данный момент распространенные языки программирования разрабатывались десятилетиями и далеко не одним человеком. Собсно, чтобы сделать что-то свое на таком же уровне потребуется столько же времени. А вероятность того, что оно будет лучше того что есть очень мала. А самое смешное, что то чего хочется уже давно есть и лежит под ногами, только not invented here все равно силен.

  • look4awhile

    2% оверхеда инициализации переменных это, например, class members при new. те, которые как-бы не обязательно очищать. итд итп.

    лиф можно выделять пулом с free-list. тогда будет ровно 0 байт оверхеда. и O(1) время аллокации. для custom объектов такое делается очень и очень часто, чтобы не тормозило. типичный пример – те-же партиклы.

    я плохо знаю как OCaml байндится к C++ или ассемблеру. переписать математику на SSE без оверхеда можно? переписать выделения памяти для конкретного класса можно?

    игры пишутся на С++ не только и не столько из-за NIH. проблемы с памятью и производительностью как-то отсрочить можно стало только недавно. скриптs и прочие DSL попытки убежать от С++ – они не от хорошей жизни. С++ для игровой индустрии не совсем давно считался чрезмерно “тяжелым” – из-за тех-же самых проблем с производительностью и памятью.

    чем дальше – тем больше можно позволить abstraction penalty – и сэкономить на цене разработки.

    тот-же OCaml вполне можно использовать вместо скрипта. и написать на нём большую часть игровой логики тоже вполне себе можно – видел несколько попыток, часть успешные. прямо сейчас масштабируется такое развлечение гораздо хуже чем С++ – большей частью из-за мультиплатформенности, и, наверняка, можно вылечить.

    оправдано или нет сочинять свой язык – очень спорный вопрос. скажем DSL часто оправданы, а насколько он “общего положения” – вопрос философский.

    в той-же игровой индустрии есть несколько очень успешных историй такого сочинительства. кроме того сесть на С++ как на платформу – куда как более простое развлечение, чем написать всю кодогенерацию с нуля (на такое реально надо десятки человеколет). для примера DSL на .net платформу можно написать давольно быстро.

  • http://vshabanov-ru.blogspot.com/ vshabanov

    Байндинги примерно так:
    value foo( value bar, value baz )
    {
    CAMLparam2( bar, baz ); // макры для сборщика мусора, если нет аллокаций не обязательны

    CAMLreturn( … );
    }
    А в кемле:
    external foo : bar_type -> baz_type -> return_type = “foo”

    Так что переписать отдельные SSE ф-ии смысла не имеет, т.к. все сожрется на вызовах ф-ии. Да и я уже говорил, что числодробильня — удел С. А вот более солидный кусок (как раз с числодробильным циклом) вполне можно вытащить.

    Про производительность — это да. Я бы с радостью писал на хаскеле, но пока пишу на кемле, т.к. побаиваюсь всяких оверхедов с ленивостью, хотя чувствую что зря. К тому же хаскел все-таки компилится в С а не в нативный код. От этого хаскел тормознее, но зато более легко портируемый. И да, ни кемла ни хаскела под cell нет. PowerPC они поддерживают, но тот что на маках (g5,g6,64), не знаю какой на х-боксах.

    Собственно я кемл и использую в качестве скрипта. И очень доволен. Если бы у меня было побольше 3Д экспы, я бы и рендер на нем написал (что и делал в предыдущей простенькой 2D игрульке), пока пользуюсь готовым плюсовым.

    Про DSL. Он все-таки подразумевает создание всей цепочки лексер => парсер => проверялка => копилялка или интерпретатор. А это нифига не просто. Даже для абсолютно кастрированного очень-кемлоподобного языка, который даже не компилился и не интерпретировался, а просто в результате разбора создавались ф-ии (в кемле это легко), мне понадобилось несколько недель. И потом я еще парился с ошибками, т.к. не было их нормального вывода. Короче, пожалел я о DSL, да и не нужен он в конце концов оказался.

    А вот DSEL, который Embedded labguage — это совсем другое дело, т.к. абстракции предметной области строятся прямо в языке и париться не надо. Проблема только в том, что язык должен быть достаточно высокоуровневым, чтобы позволять делать такие абстракции. А С++ к таким не относится. Можно, конечно, template metaprogramming навернуть, но читать километровые сообщения об ошибках и ждать полчаса компиляции одного модуля — эт сликом.

  • look4awhile

    я пробовал на хаскеле – и не осилил. шибко большой оверхед от всякого. OCaml куда более внятно на этом фоне выглядит.

    порт OCaml на PS3 PPU я видел – живой и работающий, поднят давольно небольшими усилиями группой энтузиастов. на XBOX не видел, но думаю можно. как скрипт – вполне себе канает.

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

    для компилирующей части в DSL можно использовать “платформу”. т-е даже на С++ компилировать можно, или на .net – чем значительно сэкономить себе работу. в любом случае “для себя” смысла писать DSL крайне мало, но он может очень хорошо масштабироваться вместе с проблемой – и тогда себя окупать на больших разработках.

    выпилиывать лобзиком по шаблонам я и сам ловко умею – и, уже, очевидно не буду. безусловно гадкое это мероприятие. DSEL в С++ нормально не встроить.

  • http://vshabanov-ru.blogspot.com/ vshabanov

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

    А этот порт где-то валяется? Что-то гугль сходу ничего не находит.

    Насчет рендера не очень понятно, что именно подразумевается под масштабируемостью? Основная числодробильня — это все-таки физика, для которой итак есть куча middleware, писанном на С. Работа с указателями — это самая родная тема для кемла, т.к. там все на них (просто не заметно). А непосредственно рендер — это по сути различные отсечения видимости, трансформации да пуляние команд на видюху. Т.е. там больше логики, чем number-crunching-а. А кемл для этого подходит лучше чем плюсы.

    Исплоьзовать “платформу” для DSL действительно легче, чем с нуля. Собсно для тех же хаскелов и кемлов есть Template Haskell, camlp4 (p4 = pre-pro-ce-ssor) или MetaOCaml. Но все-таки лучше DSEL-ом ограничиваться, хотябы потому, что сообщения об ошибках понятнее (это я не про плюсы ;).

  • look4awhile

    порт? нигде.

    не могу сказать что есть “основная числодробильня”. есть физика, коллизии, партиклы, рендер (определения видимости), звук, анимация, итд итп. и все достаточно сложные вычислительно. С++ на всех этих задачах рвёт OCaml, к сожалению, в разы. например “пуляние комманд на видюху” инлайнится на сколь угодно внятной платформе (не PC). а на PC действительно пофиг – доблестный D3D драйвер съест почти всю разницу. т-е на отсечении видимости, трансформациях итп удастся выиграть только 2-3 раза после OCaml. происходит это частью за счёт SSE, частью за счёт inline-а, частью за счёт числодробления.

  • http://vshabanov-ru.blogspot.com/ vshabanov

    А чего энтузиасты порт не выложили? Из-за NDA что-ли. Кстати, он только только для интереса делался, или на нем игру разрабатывали?

    По сути “физика, коллизии, анимация, звук” — для этого всего есть middleware написанный на C. В принципе то же есть и для рендера, но без всего предыдущего один рендер уже не такая ресурсоемкая задача. И повторюсь, я пользуюсь готовым плюсовым. Переписывать его на кемл — много лишней работы. Просто иногда кажется, что для конкретной задачи можно было сделать проще и на кемле.

    Инлайнятся команды на видюху — там даже драйвера что-ли нет? Все как в старом досе?

    Еще, кстати, у кемла есть огромный недостаток — у него single threaded GC, из-за чего окемловский код не может выполняться больше чем в одной нити одновременно. Так что по-любому для распараллеливания задач надо вызывать сишный код.

    И собно про ЕЗЫГ. Если еще не читал, рекомендую размышления Тима Свини про ЕЗЫГ:
    http://www.cs.princeton.edu/~dpw/popl/06/Tim-POPL.ppt
    http://lambda-the-ultimate.org/node/1277 — А тут куча комментов и несколько (особенно про GC) от самого Свини.

  • look4awhile

    из-за NDA. игру пока не разрабатывали, будут или нет – или кусками – или как – пока не знаю.

    middleware есть, правда не везде внятный, и совсем не для всех платформ.
    хуже – часто совсем плохо расширяемый.
    т-е если хочется “умом отличаться” – то какие-то куски надо писать самим, как минимум.
    звук пожалуй самый проработанный из всего, что есть.

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

    на консолях драйвера по большому счёту нет. т-е строишь набор комманд на видеокарту, а потом вызываешь их.
    можно вот тут почитать http://blog.gamedeff.com/?p=57 – про то с чем в тоге придётся работать.

    за линки спасибо. читал, но они явно в плюс.
    надо, пожалуй, сделать пост “линки по теме”

  • VoidEx

    Вот ужас :( Потрите коммент, как знак меньше-то поставить?
    В общем, функция там
    f{n:nat}(as:[]int,i:nat(less)n)=as[i]
    Может, имелось ввиду
    f{n:nat}(as:[n]int,i:nat(less)n)=as[i]
    Тогда все на свои места встает.

    П.С. (less) – это знак меньше, второй раз рисковать не стал просто :)

  • Deemetrius

    >расширять тяжело. т-е неочевидно как написать, например, функцию – которая будет работать с классом, неизвестным на момент написания функции.

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

  • Deemetrius

    Кстати в ф-ции
    switch bool Find ( Tree * root, const char * text )
    Вычисляется длина искомого текста каждый раз. Можно после первого измерения длины рекурсивно передавать текст с длиной в качестве пары.
    case TreeTypeNode:
    {
    TreeNode * node = (TreeNode *) root;
    size_t len= strlen(text);
    pair pa= pair(text, len);
    if ( lenlength )
    return TreeFind ( node->lessEqual, pa );
    else
    return TreeFind ( node->greater, pa );
    }
    Тогда ф-ция будет принимать множественный тип
    switch bool Find ( Tree * root, const char * text | pair pa :{ text= pa.first; len= pa.second } )

    off: А как вызвать виджет вставки кода?

  • deemetrius

    Я не знаю как писать в БЛОГ, поэтому напишу тут коммент.

    (То, что в скобках это мысли вслух. их можно не читать.)
    (что после двоеточия-и-пробела это имя файла)

    Написал (почти) variant , используя variadic-templates.
    Впринципе могу (но неочень хочу) переписать в стиле Loki::Typelist (Лонли Локли)

    Значит примерчик использования “варианта”: main.cpp
    http://paste.defun.ru/f3773d773

    Реализация поехала: variant.h
    http://paste.defun.ru/f2fb21670

    Реализация использует мои траиты для “variadic templates” (c++0x): vtypelist.h
    http://paste.defun.ru/f17509572
    Собственно оттуда только VTL::get_pos используется. (но если кому интересно, там ещё кое-что)

    Ну и совсем немного вспомогат. типов: types.h
    http://paste.defun.ru/f287891cb
    Они там все используются (вродекак).

    Конечно надо немного реализацию допилить. (Кпримеру написать метод unset), Но пока итак работает!

    post-scriptum мой имэйл sm0ke999@yandex.ru
    (он вроде в профиле указан, но х.з. можно ли его там увидеть?)

  • deemetrius

    Ах дааа… Компилятор был использован gcc version 4.3.2 (Ubuntu 4.3.2-1ubuntu11) –build=x86_64-linux-gnu
    Если раскоментировать
    /*case VARIANT_IS(v, char):
    std::cout () , xi::variant&)’|
    /media/all/prj/xi/variant/main.cpp|29|error: no matching function for call to ‘xi::variant::get()’|
    ||=== Build finished: 2 errors, 0 warnings ===|

  • deemetrius

    Ах дааа… Компилятор был использован gcc version 4.3.2 (Ubuntu 4.3.2-1ubuntu11) –build=x86_64-linux-gnu
    Если раскоментировать
    /*case VARIANT_IS(v, char):
    std::cout (shift-left) “char: ” (shift-left) v.get() (shift-left) std::endl;
    break;*/

    То получим ошибку компиляции (Да здравствует enable_if !!!)
    /media/all/prj/xi/variant/main.cpp||In function ‘int main()’:|
    /media/all/prj/xi/variant/main.cpp|28|error: no matching function for call to ‘search(types::type_to_type, xi::variant{int, double, my_test}&)’|
    /media/all/prj/xi/variant/main.cpp|29|error: no matching function for call to ‘xi::variant{int, double, my_test}::get()’|
    ||=== Build finished: 2 errors, 0 warnings ===|

    Членнарыло знаки меньше съеденынах

  • deemetrius

    Кстати что значит “Comment awaits moderation” ?