Объясните, а?

Comments

Как с этой штукой работать-то?

А в чём проблема?

f (P x _) (P _ g) = g x

или что-то в этом роде. Вложенность произвольная.

В общем, я почти сделал, но мне стало лень доводить до конца.

Суть в том, что в Хаскеле тип написан b, но его нельзя перенести как шаблонный параметр в Си++. Отличие в том, что в Си++ если есть шаблонный параметр типа, он точно известен. Тут же b как бы любой. Т.е. в Си++ в качестве аналога надо брать void*. Тогда cdr будет иметь вид void * -> P<std::pair<A, void*> >. + пишем шаблонную (уже невиртуальную) обёртку, которая эти void* преобразует в переданный B. Ошибки нет, так как мы в функцию передаём B, откастованный в void*, а полученный обратно void* - это тот самый B и есть (так же и в Хаскеле, с параметром неизвестного типа особо не наиграешься, только вернуть обратно в какой-либо комбинации) :) И мы кастуем его обратно.

Проблема в том, что мы назад ещё и функцию получаем (у меня был boost::function) и кастовать её и думать дальше мне стало лень, да и компилятор я этим кодом в итоге вроде подвесил.

> Отличие в том, что в Си++ если есть шаблонный параметр типа, он точно известен.

Начиная с этой фразы, я перестал понимать.

Этот код неэквивалентен Хаскельному:

template <class B> virtual P<std::pair<A,B> > cdr (B) = 0;
Потому что вы знаете конкретный тип B и можете с разными типами делать разное. В Хаскеле - нет.

Эквивалентная замена:

P<std::pair<A, void*> > cdrImpl(void *)

И обёртка над ним:

template <class B> P<std::pair<A, B> > cdr(B b)

{

P<std::pair<A, void *> > retImpl = cdrImpl(static_cast<void *>(&b));

P<std::pair<A, B> > ret;

ret.car.first = retImpl.car.first;

ret.car.second = *static_cast<B *>(retImpl.car.second);

ret.cdrImpl = // как-нибудь кастануть и функцию

return ret;

}

И далее развитие этой мысли.

> Потому что вы знаете конкретный тип B

Чего?
Внутри cdr

тип функции a -> a в Хаскеле не эквивалентен типу

template <class A> A f(A a) в Си++

Он эквивалентен типу

void * f(void *)

поэтому и переписывать надо соответствующе

> тип функции a -> a в Хаскеле не эквивалентен типу

> template <class A> A f(A a) в Си++

Вот это я опять не понимаю.

То есть, нет, частично понимаю. Например, мы можем использовать что-нибудь типа a.doSomething() внутри тела f. То есть, реально это будет что-то типа (C a) => a -> a, где C - специальным образом подобранный класс. Эта разница имеется в виду?

> void * f(void *)

Нет, ну, поиметь проверку типов и я могу, это неинтересно.

А тип a -> b -> a чему эквивалентен, по-вашему?

По типу a -> a я вам даже реализацию напишу. И по типу a -> b -> a тоже. А по Си++сным шаблонам сказать нельзя решительно ничего, так как для каждого типа реализация может быть вообще своя.

Поэтому a -> b -> a вообще в Си++ не выражается. Наиболее близко:

void * f(void *, void *). Формально, даже void * f(void *) это по сути a -> b, так как указать, что на самом деле внутри void * лежит нечто того же типа, что и передали - никак нельзя.

Т.е. void* более слабо, так как теряется информация о типах вообще, а template более сильно, так как вся информация есть. Хаскельное a нечто среднее, так как с одной стороны тип-то мы не знаем, но мы можем по крайней мере указать, что принимаются два одинаковых и что возвращается такой же.

Видимо, полная эквивалетность вот такая:

a -> b -> a

void * fooImpl(void *, void *); // имплементация, внутри которой как раз типов и нет уже

template <typename A, typename B>

A foo(A, B) { fooImpl(...); } // обёртка для типизации

>Нет, ну, поиметь проверку типов и я могу, это неинтересно.

Т.е. речь по сути о том, что типизации разные в принципе. Поэтому иметь типизацию придётся, но частично.

Напрямую не переносится код ни туда, ни обратно.

Как например перенести с Си++ на Хаскель:

template <class A> A foo(A)

?

> По типу a -> a я вам даже реализацию напишу.

Одну?

> template <class A> A foo(A)

Ну дык foo :: a -> a. Оно понятно, что C++-ный вариант позволяет до жопы вариантов - см. выше про doSomething. Ну тогда можно, опять-таки, foo :: C a => a -> a.

Вопрос-то не в том, как типы переносить. Вопрос в том, как переносить конкретный КОД. Вот если вы мне напишете функцию foo, то я её запишу на хаскеле, задав тип как в предыдущем абзаце. А вот функцию f из моего первого коммента в этой дискуссии можно (по вашему) перенести в C++ только с грязными хаками. Вот я и хочу без них обойтись.

>Одну?

Рабочую одну. Ну там ещё error, undefined, throw и прочее из этой оперы. Или я чего-то не знаю? (я именно про a -> a, а не C a => a -> a)

>Вот если вы мне напишете функцию foo, то я её запишу на хаскеле, задав тип как в предыдущем абзаце

Да разве

template <class A> foo (A)

{ millionsOfSpecializations<A>::foo(); }

И как же?

>А вот функцию f из моего первого коммента в этой дискуссии можно (по вашему) перенести в C++ только с грязными хаками. Вот я и хочу без них обойтись.

Смотря что считать грязным хаком. B -> void * -> B - well-defined.

Если же сделать P<A, F>, где F - template <typename B> class, то будет и без хаков, но шаблон P станет зависеть так же и от функции. Это продиктовано именно тем, что в Си++ шаблон позволяет больше, чем a -> P (a, b) в Хаскеле.

Т.е. a -> b -> a по сути ничего не знает о типах в реализации. Но такая запись позволяет наложить некоторые ограничения (тип результата тот же, что и у первого аргумента). В Си++ это не выражается в принципе, а шаблон мощнее, так как в реализации шаблона мы знаем типы, и можем даже на этом знании основывать реализацию. Так что выбор - или делать мощнее (и пропихивать лишний шаблонный параметр), или нарушить ограничение в потрохах, а пропихнуть только наружу обёрткой, так как выразить его (ограничение) невозможно.

>А вот функцию f из моего первого коммента в этой дискуссии можно (по вашему) перенести в C++ только с грязными хаками

Не обратил внимания, кстати. Думал, Вы про функции из поста. Эту - можно:

template <class A, class B>

P<std::pair<B, A> > f(P<A> px, P<B> pg) { return pg.cdr(px.car); }

Post a comment

Already a Vox member? Sign in

migmit

About Me

migmit
Russian Federation
  • Powered by Vox