Re: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 28.06.10 08:21
Оценка: 236 (12)
Здравствуйте, WolfHound, Вы писали:

WH>Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.


То, чем ты занимаешься, называется "извращение". Присоединюсь, если ты не против

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, IncoherentInstances, TypeFamilies #-}

data Box a = Box a

class Boxed b v where
    unwrap :: b -> v

instance Boxed b v => Boxed (Box b) v where
    unwrap (Box x) = unwrap x

instance (l ~ r) => Boxed l r where
    unwrap x = x

> unwrap (Box(Box(Box(Box(123)))))
123
Этюд Unbox(Box<Box<...Box<T>...>> )
От: WolfHound  
Дата: 26.06.10 22:55
Оценка: 25 (2)
В форуме .НЕТ запостили вот такой этюд
Автор: nikov
Дата: 26.06.10
.
В рамках системы типов .НЕТ данный этюд статически типизированно не решается.
Но он по зубам С++:
template<class T>
struct Box
{
private:
    T _value;

public:
    T Get() const
    {
        return _value;
    }

    Box(T const& value)
        : _value(value)
    {}
};

template<class T>
Box<T> box(T const& value)
{
    return Box<T>(value);
}

template<class T>
struct UnwrapBoxedType
{
    typedef T type;
};

template<class T>
struct UnwrapBoxedType<Box<T> >
{
    typedef typename UnwrapBoxedType<T>::type type;
};

template<class T>
T unwrap(T const& value)
{
    return value;
}

template<class T>
typename UnwrapBoxedType<T>::type unwrap(Box<T> const& box)
{
    return unwrap(box.Get());
}

int main()
{
    std::cout << unwrap(box(box(box(box(box(box(123))))))) << std::endl;
    return 0;
}

UnwrapBoxedType это функция осуществляющая вычисления над типами. Она достает тип значения самой вложенной коробки.

Однако система типов С++ весьма не формальна и все проверки осуществляются в момент вызова unwrap.

Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: deniok Россия  
Дата: 28.06.10 07:20
Оценка: 10 (2)
Здравствуйте, vdimas, Вы писали:

D>>
>>> unwrap (Box (Box (Box (Box (Value 123)))))
D>>123
D>>


V>Ну, введя доп. рантайм флаг (дискриминатор алг. типа) можно на любом языке аналогично сделать.

V>Хотелось без рантайм-костылей.

Ну тогда это уже не что иное как оптимизация; напишем правила, аннигилирующие unwrap'ы и явные применения конструкторов боксов:
{-# RULES
"unwrap/box"    forall b. unwrap (Box b) = unwrap b
"unwrap/value"  forall v. unwrap (Value v) = v
  #-}

откомпилируем с ключиками -o2 (оптимизация) и -ddump-simpl-stats (чтобы посмотреть, что правила отработали). Получим для
main = do putStrLn test 
test = unwrap (Box (Box (Box (Box (Value "foo")))))

дамп статистики
==================== Grand total simplifier statistics ====================
Total ticks:     36

8 PreInlineUnconditionally
4 UnfoldingDone
7 RuleFired
    1 unpack
    1 unpack-list
    4 unwrap/box
    1 unwrap/value
2 LetFloatFromLet
15 BetaReduction
10 SimplifierDone
Re[5]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Artifact  
Дата: 27.06.10 21:55
Оценка: 5 (1)
Здравствуйте, WolfHound, Вы писали:

WH>Нука покажи как это можно сделать без использования UnwrapBoxedType.


Ну в GCC 4.4 (я пользуюсь MinGW под Виндой сейчас) с опцией -std=c++0x можно заставить компилятор вывести тип

template<class T>
struct Box
{
private:
    T _value;

public:
    T Get() const
    {
        return _value;
    }

    Box(T const& value)
        : _value(value)
    {}
};

template<class T>
Box<T> box(T const& value)
{
    return Box<T>(value);
}

template<class T>
struct Unwrap
{
    T operator()(T const& value)
    {
        return value;
    }
};

template<class T>
struct Unwrap<Box<T>>
{    
    auto operator()(Box<T> const& box) -> decltype(Unwrap<T>()(box.Get()))
    {
        return Unwrap<T>()(box.Get());
    }
};

template<class T>
auto unwrap(Box<T> const& box) -> decltype(Unwrap<Box<T>>()(box.Get()))
{
    return Unwrap<Box<T>>()(box.Get());
}

int main()
{
    std::cout << unwrap(box(box(box(box(box(box(123))))))) << std::endl;
    return 0;
}


Только вот пришлось использовать структуры. С функциями decltype отправляет компилятор в аут.
__________________________________
Не ври себе.
Re[6]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 29.06.10 10:02
Оценка: :)
lomeo,

LCR>> Правильно?

L>Так точно!
Спасибо, брат!
... << RSDN@Home 1.2.0 alpha 4 rev. 1423>>
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re: Этюд Unbox(Box<Box<...Box<T>...>> )
От: gear nuke  
Дата: 27.06.10 02:39
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Однако система типов С++ весьма не формальна и все проверки осуществляются в момент вызова unwrap.


WH>Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.


Можно более подробно описать проблему для ммм... приплюснутых мозгов?

Проверка типа в момент вызова (инстанциации) функции происходит при использовании специализаций шаблона.

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

Немного упрощу предложенное решение, что бы это стало очевидно:
int unwrap(int const& value)
{
    return value;
}

template<class T>
typename UnwrapBoxedType<T>::type unwrap(Box<T> const& box) 
{
    return unwrap(box.Get());
}
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re: Этюд Unbox(Box<Box<...Box<T>...>> )
От: FR  
Дата: 27.06.10 05:19
Оценка:
Здравствуйте, WolfHound, Вы писали:


WH>Однако система типов С++ весьма не формальна и все проверки осуществляются в момент вызова unwrap.


WH>Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.


На D можно запихнуть в одну compile time функцию, но по сути будет тоже что и в C++ проверка при инициализации.
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: WolfHound  
Дата: 27.06.10 10:58
Оценка:
Здравствуйте, gear nuke, Вы писали:

WH>>Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.

GN>Можно более подробно описать проблему для ммм... приплюснутых мозгов?
Честно говоря не понимаю куда подробнее.
Проблема в том что все проверки осуществляются вот в этом месте
unwrap(box(box(box(box(box(box(123)))))))
А хочется чтобы к этому моменту уже все было проверено

GN>Проверка типа в момент вызова (инстанциации) функции происходит при использовании специализаций шаблона.

GN>В данном случае специализации шаблона нет, есть 2 перегруженные функции, тип агрумента которых уже известен в момент определения.
А ты хорошо смотрел?
А это что?
typename UnwrapBoxedType<T>::type
Также не стоит забывать вот про эту волшебную строку
return unwrap(box.Get());
разрешение перегрузки опять же происходит в момент вызова функции.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: vdimas Россия  
Дата: 27.06.10 12:59
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Проблема в том что все проверки осуществляются вот в этом месте

WH>unwrap(box(box(box(box(box(box(123)))))))
WH>А хочется чтобы к этому моменту уже все было проверено

Задай в Box<> целочисленный параметр-счетчик, определяющий глубину вложения, и перепиши специализации UnwrapBoxedType<> с рекурсивным использованием счетчика, типа ручная проверка.

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

WH>разрешение перегрузки опять же происходит в момент вызова функции.


Хотел сказать, компиляции места вызова?

Да, проблема известная. Это из-за отсутствия частичной специализации ф-ий, вследствии чего приходится использовать пользовательские типы-хелперы, навроде твоего UnwrapBoxedType<T>. Проблема тут в том, что раз хелпер-это другой тип, не связанный с Box<>, то в момент его определения и определения шаблонной ф-ии unwrap компилятор не способен проверить соответствие типов (без того "ручного" счетчика, например). Я в похожих случаях стараюсь давать компилятору больше информации, типа:

template<typename T>
struct Box {
typedef T value_type;
typedef T most_wrapped_type;
...
};

template<typename T>
struct Box< Box<T> > {
typedef Box<T> value_type;
typedef typename Box<T>::most_wrapped_type most_wrapped_type;
...
};

А в самом хелпере UnwrapBoxedType просто перенаправить на Box<>::most_wrapped_type, чтобы уменьшить вероятность ошибки. (Рассматриваемый пример конечно примитивен и не нуждается в этом, но это я об общей практике говорю).

Правда, MS VC все-равно все нафиг игнорирует, т.е. не проверяет толком шаблоны. Дык, зато GCC с версий 4.x более внимательно их проверяет, и частенько ругается на всякие несоответствия еще до инстанциирования, т.е. в момент компиляции самих шаблонных определений, чем приятно радует.

Конечно, нужны явные constrains. До определенной степени их можно эмулировать (и в бусте полно хелперов под распространенные случаи, довольно-таки мощный там раздел type traits), но на уровне языка, т.е. с нормальным синтаксисом, а не тонной угловых скобок, было бы интересней.
Re[4]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: WolfHound  
Дата: 27.06.10 13:49
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Задай в Box<> целочисленный параметр-счетчик, определяющий глубину вложения, и перепиши специализации UnwrapBoxedType<> с рекурсивным использованием счетчика, типа ручная проверка.

Box менять нельзя.

V>Только не понятно, зачем? Да, в момент компиляции конкретного места вызова ф-ии, компилятор сам рекурсивно проверит все типы для каждой итерации unwrap(), вплоть до своего ограничения на глубину вложенности шаблонов.

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

V>Да, проблема известная. Это из-за отсутствия частичной специализации ф-ий, вследствии чего приходится использовать пользовательские типы-хелперы, навроде твоего UnwrapBoxedType<T>.

Нука покажи как это можно сделать без использования UnwrapBoxedType.

V> Проблема тут в том, что раз хелпер-это другой тип, не связанный с Box<>, то в момент его определения и определения шаблонной ф-ии unwrap компилятор не способен проверить соответствие типов (без того "ручного" счетчика, например). Я в похожих случаях стараюсь давать компилятору больше информации, типа:

Ты не понял.
Мне интересно решение конкретной задачи.
Причем не абы какое, а формальное.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re: Этюд Unbox(Box<Box<...Box<T>...>> )
От: deniok Россия  
Дата: 27.06.10 14:02
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.


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

Почему бы, например, не маркировать завершение рекурсии по типам явно, поименовав конструктор небоксированных данных?
data Boxes a = Value a | Box (Boxes a)

unwrap :: Boxes t -> t
unwrap (Box b)   = unwrap b
unwrap (Value v) = v


> unwrap (Box (Box (Box (Box (Value 123)))))
123
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: WolfHound  
Дата: 27.06.10 15:00
Оценка:
Здравствуйте, deniok, Вы писали:

D>Почему бы, например, не маркировать завершение рекурсии по типам явно, поименовав конструктор небоксированных данных?

Так не интересно.
Ибо вот эти значения имеют один тип.
(Box (Value 123))
(Box (Box (Value 123)))
В исходной задаче они имеют разные типы.

Хочется вот такое
data Box a = Box a

unwrap :: ??? -> t
???


unwrap (Box(Box(Box(Box(123)))))

Хочется посмотреть на язык в котором такое можно.
Ибо что-то мне подсказывает что если можно такое то система типов позволит еще много интересного.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[5]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: vdimas Россия  
Дата: 27.06.10 18:21
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Мне интересно решение конкретной задачи.

WH>Причем не абы какое, а формальное.

Из очевидных формальных — с положительным целочисленным счетчиком в Box и генерацией ошибки на некоем count==some_max. А не меняя Box<> решить в общем виде ИМХО в принципе невозможно, хотя бы из-за потенциальной бесконечной вложенности.

В типах дотнета красивое и строго типизированное решение вообще не представимо. У меня была абсолютно идентичная задача, так вот, без всяких динамиков она решалась примерно так:
interface IBoxedValue { object Value { get; } }
interface IBoxedValue<T> { T Value { get;} }

class Box<T> : IBoxedValue<T>, IBoxedValue { ... }

object GetRealObject(object o) {
  object tmp;
  while(null != (tmp=(o as IBoxedValue)))
    o = tmp.Value;

  return o;
}


К тому же, интерфейс IBoxedValue использовался не только в Box<>, бо прием типа ISomethingProvider удобен в массе различных вариаций.
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: vdimas Россия  
Дата: 27.06.10 18:26
Оценка:
Здравствуйте, deniok, Вы писали:

D>Почему бы, например, не маркировать завершение рекурсии по типам явно, поименовав конструктор небоксированных данных?

D>
D>data Boxes a = Value a | Box (Boxes a)

D>unwrap :: Boxes t -> t
D>unwrap (Box b)   = unwrap b
D>unwrap (Value v) = v
D>


D>
>> unwrap (Box (Box (Box (Box (Value 123)))))
D>123
D>


Ну, введя доп. рантайм флаг (дискриминатор алг. типа) можно на любом языке аналогично сделать.
Хотелось без рантайм-костылей.
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: night beast СССР  
Дата: 28.06.10 06:13
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>>>Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.

GN>>Можно более подробно описать проблему для ммм... приплюснутых мозгов?
WH>Честно говоря не понимаю куда подробнее.
WH>Проблема в том что все проверки осуществляются вот в этом месте
WH>unwrap(box(box(box(box(box(box(123)))))))
WH>А хочется чтобы к этому моменту уже все было проверено

ок. где по твоему все уже должно быть проверенно? (в каком месте)
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: deniok Россия  
Дата: 28.06.10 07:42
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Хочется вот такое

WH>
WH>data Box a = Box a

WH>unwrap :: ??? -> t
WH>???


WH>unwrap (Box(Box(Box(Box(123)))))
WH>

WH>Хочется посмотреть на язык в котором такое можно.

"Такое" мне непонятно. unwrap — это функция, то есть ??? — это тип. Задача — свернуть семейство возможных здесь тИповых конструкций в замкнутую форму для этого типа. Вариант ad hoc множества типов не очень элегантен (ручками писать все возможные перегрузки — задолбаешься) и, вроде, ни кем и не предлагается. Я предложил классический рекурсивный тип; можно ещё забацать индексированное натуральными числами семейство.
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: deniok Россия  
Дата: 28.06.10 08:23
Оценка:
Здравствуйте, lomeo, Вы писали:

L>
L>{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, IncoherentInstances, TypeFamilies #-}

L>instance (l ~ r) => Boxed l r where
L>    unwrap x = x

L>


Где ты научился пользоваться ~ ? Я тоже хочу!
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 28.06.10 08:33
Оценка:
Здравствуйте, deniok, Вы писали:

D>Где ты научился пользоваться ~ ? Я тоже хочу!


Не надо, это изврат, лучше задачу переформулировать.
Re[4]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: vdimas Россия  
Дата: 28.06.10 11:46
Оценка:
Здравствуйте, deniok, Вы писали:

Интересная фича, но сдается мне, она возможна только если компилятор "видит" как формировался вложенный Box.
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: gear nuke  
Дата: 28.06.10 20:31
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>А это что?

WH>typename UnwrapBoxedType<T>::type

Это "вытащенный из коробки" тип T. Он уже известен в момент определения функции.

WH>Также не стоит забывать вот про эту волшебную строку

WH>return unwrap(box.Get());
WH>разрешение перегрузки опять же происходит в момент вызова функции.

Оно происходит когда компилятор смотрит тело функиции, то есть в момент определения.

Можно добавить явное инстанциирование (и проверки) перед вызовом:
auto p = &unwrap<Box<Box<Box<Box<Box<int>>>>>>;


Это можно скомпилировать и положить в отдельный модуль. Однако есть проблема с неограниченной вложенностью.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 29.06.10 08:00
Оценка:
lomeo,

Это чего такое, можешь объяснить?
L>instance (l ~ r) => Boxed l r where
L>    unwrap x = x

Ибо такую синтаксическую конструкцыю я встречаю впервые.
... << RSDN@Home 1.2.0 alpha 4 rev. 1423>>
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 29.06.10 08:18
Оценка:
LCR>
L>>instance (l ~ r) => Boxed l r where
L>>    unwrap x = x
LCR>

LCR>Ибо такую синтаксическую конструкцыю я встречаю впервые.

Нашёл, магия под названием equality constraints, в статье "Type Checking with Open Type Functions"...
... << RSDN@Home 1.2.0 alpha 4 rev. 1423>>
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 29.06.10 08:18
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>Это чего такое, можешь объяснить?


http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/type-families.html#id663186

Прикол в том, насколько я понимаю, что в случае

instance Boxed v v


унификация не проходит.

Например unwrap True не догадается сопоставить второй v с Bool.

Унификация возможна только в констрейнтах, поэтому мы выносим равенство типов в констрейнт

instance (l ~ r) => Boxed l r


По сути это эквивалентно первому инстансу, только теперь компилятор будет верно выводить типы.
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: anton_t Россия  
Дата: 29.06.10 08:28
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>lomeo,


LCR>Это чего такое, можешь объяснить?

LCR>
L>>instance (l ~ r) => Boxed l r where
L>>    unwrap x = x
LCR>

LCR>Ибо такую синтаксическую конструкцыю я встречаю впервые.

Я так полагаю http://www.haskell.org/haskellwiki/GHC/Type_families , конкретно пункт 6.3.
Re[4]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 29.06.10 09:37
Оценка:
lomeo,

instance Boxed b v => Boxed (Box b) v where
    unwrap (Box x) = unwrap x

instance (l ~ r) => Boxed l r where
    unwrap x = x

А, то есть если b и v не совпадают (то есть когда мы находимся в середине цепочки вскрытия Box-ов), то будет использован первый инстанс (точнее, unwrap из первого инстанса), а когда l совпадает с r (то есть последнее, холостое вскрытие для конкретного 123), то будет использован второй инстанс. Правильно?
... << RSDN@Home 1.2.0 alpha 4 rev. 1423>>
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[4]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 29.06.10 09:39
Оценка:
anton_t,

_>Я так полагаю http://www.haskell.org/haskellwiki/GHC/Type_families , конкретно пункт 6.3.


Да, спасибо, из статьи как раз и вышел на семейства типов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1423>>
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[5]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 29.06.10 09:55
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR> Правильно?


Так точно!
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: WolfHound  
Дата: 29.06.10 13:08
Оценка:
Здравствуйте, lomeo, Вы писали:

L>
L>instance Boxed b v => Boxed (Box b) v where
L>    unwrap (Box x) = unwrap x

L>instance (l ~ r) => Boxed l r where
L>    unwrap x = x
L>

Интересно но не понятно почему работает.
Дело в том что второй инстенс подходит для для всех типов и как следствие должна быть неоднозначность.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 29.06.10 14:48
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Интересно но не понятно почему работает.

WH>Дело в том что второй инстенс подходит для для всех типов и как следствие должна быть неоднозначность.

IncoherentInstances работают как OverlappingInstances.
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Аноним  
Дата: 02.07.10 00:27
Оценка:
Здравствуйте, lomeo, Вы писали:

L>То, чем ты занимаешься, называется "извращение". Присоединюсь, если ты не против


Интересно, а вот такое можно как-нибудь без функциональных зависимостей заставить работать?
class Funct a b c d where
    f_map :: (a -> b) -> c -> d

instance (Functor m, Funct a b c d) => Funct a b (m c) (m d) where
    f_map = fmap . f_map

instance (a ~ c, b ~ d) => Funct a b c d where
    f_map = id
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 02.07.10 10:52
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Интересно, а вот такое можно как-нибудь без функциональных зависимостей заставить работать?


А сейчас не работает? Можно пример?
Re[4]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Аноним  
Дата: 02.07.10 12:11
Оценка:
Здравствуйте, lomeo, Вы писали:

L>А сейчас не работает? Можно пример?


Неа:

f_map not [True]

<interactive>:1:0:
Couldn't match expected type `[Bool]' against inferred type `Bool'
When generalising the type(s) for `it'

Re[5]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: deniok Россия  
Дата: 02.07.10 22:28
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, deniok, Вы писали:


V>Интересная фича, но сдается мне, она возможна только если компилятор "видит" как формировался вложенный Box.


Да, но Simon Peyton Jones, похоже, взялся за суперкомпиляцию. Так что, возможно, GHC вскоре будет "видеть" довольно далеко.
Re[5]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 05.07.10 13:10
Оценка:
Здравствуйте, Аноним, Вы писали:

L>>А сейчас не работает? Можно пример?


А>Неа:


При явном указании типа работает. Если же явного указания нет, то мы ничего не сделаем. Тут такой прикол. Несмотря на ограничения инстанса GHC выбирает инстанс исходя только и исключительно из его заголовка — Fucnt a b c d или Funct a b (m c) (m d). Тут даже Overlapping(Incoherent)Instances не поможет. Я подумаю, что тут можно сделать, но, как мне кажется, лёгкого пути здесь нет.
Re[6]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Аноним  
Дата: 05.07.10 14:29
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Несмотря на ограничения инстанса GHC выбирает инстанс исходя только и исключительно из его заголовка — Fucnt a b c d или Funct a b (m c) (m d).


Что из заголовка, это понятно, но не понятно чем, в принципе отличаются:
instance (Functor m, Funct a b c d) => Funct a b (m c) (m d) where
    f_map = fmap . f_map

и
instance Boxed b v => Boxed (Box b) v where
    unwrap (Box x) = unwrap x


Почему для
f_map not [True]

выбирается, казалось бы, более общий случай
instance (a ~ c, b ~ d) => Funct a b c d where

а должен выбираться "more specific" (на мой взгляд — первый).
Re[7]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 06.07.10 06:59
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Что из заголовка, это понятно, но не понятно чем, в принципе отличаются:

А>
А>instance (Functor m, Funct a b c d) => Funct a b (m c) (m d) where
А>    f_map = fmap . f_map
А>

А>и
А>
А>instance Boxed b v => Boxed (Box b) v where
А>    unwrap (Box x) = unwrap x
А>


У меня же явный тип указан.

А>Почему для

А>
А>f_map not [True]
А>

А>выбирается, казалось бы, более общий случай
А>
А>instance (a ~ c, b ~ d) => Funct a b c d where
А>

А>а должен выбираться "more specific" (на мой взгляд — первый).

More specific выбирается по шапке. a b c d ничем не хуже в этом случае, чем a b (m c) (m d), т.к. kind на специфичность не влияют.
Re[8]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Аноним  
Дата: 06.07.10 07:32
Оценка:
Здравствуйте, lomeo, Вы писали:

L>У меня же явный тип указан.


Box b?

L>More specific выбирается по шапке. a b c d ничем не хуже в этом случае, чем a b (m c) (m d), т.к. kind на специфичность не влияют.


"a b c d" и "a b [c] [d]" тоже одинаково специфичны?
class Funct a b c d where
    f_map :: (a -> b) -> c -> d

instance (Funct a b c d) => Funct a b [c] [d] where
    f_map = map . f_map

instance (a ~ c, b ~ d) => Funct a b c d where
    f_map = id
Re[9]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 06.07.10 17:46
Оценка:
Здравствуйте, Аноним, Вы писали:

А>"a b c d" и "a b [c] [d]" тоже одинаково специфичны?


Хм, спасибо.. Видимо я себе неправильно представлял выбор инстанса, либо баг в GHC.
Re[10]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Аноним  
Дата: 06.07.10 18:02
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Хм, спасибо.. Видимо я себе неправильно представлял выбор инстанса, либо баг в GHC.


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

Кстати, можно еще ваш пример вот так переписать, для наглядности:
data Box a = Box a

class Unboxable m where
    unbox :: m a -> a

instance Unboxable Box where
    unbox (Box a) = a

instance Unboxable [] where
    unbox [a] = a


class Boxed b v where
    unwrap :: b -> v

instance (Unboxable m, Boxed b v) => Boxed (m b) v where
    unwrap a = unwrap (unbox a)

instance (l ~ r) => Boxed l r where
    unwrap x = x

*Main> unwrap (Box [Box [[5]]])
5


PS А изначально-то вопрос взялся в результате медитирования вот над этим олеговским примером: deepest functor

Подозреваю, что "не тот инстанс" выбирается не из-за того, что оба одинаково "специфичны" (иначе бы было бы "Duplicate instance declarations"), а потому что "нужный" инстанс слишком специфичен (m d). А вот функциональными зависимостями это кажется чинится..
Re[11]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 06.07.10 19:40
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, lomeo, Вы писали:


L>>Хм, спасибо.. Видимо я себе неправильно представлял выбор инстанса, либо баг в GHC.

А>Не думаю что баг. Просто не так все тривиально как кажется (во всяком случае для меня).

Угу. Всё таки я откопал
http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/type-class-extensions.html#instance-overlap

Suppose that from the RHS of f we get the constraint C Int [b]. But GHC does not commit to instance (C), because in a particular call of f, b might be instantiate to Int, in which case instance (D) would be more specific still. So GHC rejects the program. (If you add the flag -XIncoherentInstances, GHC will instead pick (C), without complaining about the problem of subsequent instantiations.)


Таким образом, если мы вместо OverlappingInstances используем IncoherentInstances, то GHC игнорирует следующую ошибку:

    Overlapping instances for Funct Bool Bool [Bool] d
      arising from a use of `f_map' at A.hs:12:14-29
    Matching instances:
      instance [overlap ok] (a ~ c, b ~ d) => Funct a b c d
        -- Defined at A.hs:9:9-39
      instance [overlap ok] (Funct a b c d) => Funct a b [c] [d]
        -- Defined at A.hs:6:9-44
    (The choice depends on the instantiation of `d'
     To pick the first instance above, use -XIncoherentInstances
     when compiling the other instance declarations)
    In the first argument of `print', namely `(f_map not [True])'
    In the expression: print (f_map not [True])
    In the definition of `main': main = print (f_map not [True])


Т.е. определить d надо явно для правильного выбора инстанса, потому что в одном случае (Bool) выберется один инстанс, как более специфичный, в другом ([Bool]) выберется другой. В моём случае с Boxed, видимо, мне просто повезло, что выбирался нужный инстанс — не знаю, тут надо подумать.
Re[12]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Аноним  
Дата: 09.07.10 00:33
Оценка:
Кстати вот так вот работает:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, UndecidableInstances,
TypeFamilies, IncoherentInstances, ScopedTypeVariables #-}

class Funct a b c d where
    f_map :: (a -> b) -> c -> d

instance (Functor m, Funct a b c d, m d ~ e) => Funct a b (m c) e where
    f_map = fmap . (f_map :: (a -> b) -> c -> d)

instance (a ~ c, b ~ d) => Funct a b c d where
    f_map = id

*Main> f_map (+1) [[Just [5]]]
[[Just [6]]]
Re[13]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 09.07.10 06:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Кстати вот так вот работает:


Ну да — последний параметр уже не влияет на выбор инстанса + включён IncoherentInstances
Re[2]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 16.07.10 15:27
Оценка:
Здравствуйте, lomeo, Вы писали:

WH>>Есть ли языки где можно провернуть такой фокус проверив типы unwrap в месте ее описания, а не в месте вызова.

L>То, чем ты занимаешься, называется "извращение". Присоединюсь, если ты не против

А можно пояснить, как эта штука реализуется?

Насколько я понимаю, foo :: Class1 t, Class2 u => t -> u -> v
означает, что в функцию, наряду со ссылками на аргументы, передаются ещё и словари инстансов Class1 t и Class2 u.
То есть, вызывающая сторона — если имеет дело с конкретными типами, подсовывает конкретные словари; если типы выводятся, то выводятся и соответствующие требования получить словари у вызывающей вызывающей стороны.

В конце концов, получается, что фокус проворачивается именно в месте вызова, а не в месте описания.
И отличие от С++ шаблонов — только в том, что шаблоны, помимо объявлений, ещё и определения функций вытаскивают на божий свет.

Или я чего-то не понимаю?
Перекуём баги на фичи!
Re[3]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 16.07.10 16:09
Оценка:
Здравствуйте, Кодт, Вы писали:

Вроде всё так.

К>В конце концов, получается, что фокус проворачивается именно в месте вызова, а не в месте описания.

К>И отличие от С++ шаблонов — только в том, что шаблоны, помимо объявлений, ещё и определения функций вытаскивают на божий свет.

С++ тоже в месте вызова проворачивает фокус. Насчёт вытаскивания определения функций на божий свет не понял — что имеется в виду?
Re[4]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 16.07.10 17:33
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Вроде всё так.



К>>В конце концов, получается, что фокус проворачивается именно в месте вызова, а не в месте описания.

К>>И отличие от С++ шаблонов — только в том, что шаблоны, помимо объявлений, ещё и определения функций вытаскивают на божий свет.

L>С++ тоже в месте вызова проворачивает фокус.


А WolfHound хотел — провернуть в месте определения.
То есть, максимально разнести модули. В одном модуле — только определение Box<T>, в другом — unwrap<*> (знающий о Box), в третьем — создаём матрёшку, отправляем её в раздевалку и получаем результат.


L> Насчёт вытаскивания определения функций на божий свет не понял — что имеется в виду?


В хаскелле, как и в С++, можно определения функций давать прямо в точке объявления
instance (Boxed b v) => Boxed (Box b) v where
  unwrap (Box b) = (unsafePerformIO $ putStrLn "unwrapping Box...") `seq` unwrap b

а можно вынести в объявление модуля, сделав определение непрозрачным для пользователей. (Как это сделали, например, с монадой IO).

Но хаскелловские полиморфные функции — это, как бы, дженерики. На входе void* плюс словари, на выходе void* плюс словари. И внутри коммутация между входами и выходами словарных функций. То есть, определение можно спокойно прятать.

А С++ные шаблонные функции — это, как бы, макросы.
Программист спрятал определение -> компилятор не смог его инстанцировать для произвольного заданного типа -> компилятор решил, что программист где-то это инстанцирование сделал руками -> компилятор оставил заявку линкеру -> линкер выругался.

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

template<class T> void foo(T x)
{
  ...................
  ... bar(x,x,x); ...
  ... buz<T>();   ...
  ...................
}

///////////////////////
// в хедере

namespace foo_aux // это пользователю не потребуется - только для инстанцирования шаблона
{
  struct FooBase
  {
    void foo(void*); // паттерн "Шаблонный метод"
    // детали для метода
    virtual void bar(void*,void*,void*) = 0;
    virtual void buz() = 0;
  };
  template<class T> struct Foo : FooBase
  {
    // реализация деталей
    void bar(void* a, void* b, void* c) { ::bar(*(T*)a, *(T*)b, *(T*)c); }
    void buz() { ::buz<T>(); }
  };
}

template<class T> void foo(T x) { foo_aux::Foo<T>().foo(&x); }

/////////////////////////
// где-то глубоко в .cpp

namespace foo_aux
{
  void FooBase::foo(void* x)
  {
    .........................
    ... this->bar(x,x,x); ...
    ... this->buz();      ...
    .........................
  }
}

В нашем случае весь сок — именно в struct Foo<T> — там, где мы потрошим данные. А FooBase::foo тривиальны.
Разве что printf("Unwrapping Box...") или printf("Unwrapped to the bottom") вынести.
Перекуём баги на фичи!
Re[5]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 17.07.10 10:55
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А WolfHound хотел — провернуть в месте определения.

К>То есть, максимально разнести модули. В одном модуле — только определение Box<T>, в другом — unwrap<*> (знающий о Box), в третьем — создаём матрёшку, отправляем её в раздевалку и получаем результат.

C Haskell в чём проблема разнести на модули? (Я просто не пойму что требуется

L>> Насчёт вытаскивания определения функций на божий свет не понял — что имеется в виду?


К>В хаскелле, как и в С++, можно определения функций давать прямо в точке объявления

К>а можно вынести в объявление модуля, сделав определение непрозрачным для пользователей. (Как это сделали, например, с монадой IO).

Всё равно не понял — IO не функция. Что значит "определение функции непрозрачно для пользователя"?

К>Но хаскелловские полиморфные функции — это, как бы, дженерики. На входе void* плюс словари, на выходе void* плюс словари. И внутри коммутация между входами и выходами словарных функций. То есть, определение можно спокойно прятать.


Никаких словарных функций нет, на выходе тоже словарей нет. Использование unwrap :: (Boxed w v) => w -> v генерится во что-то вроде

-- class Boxed w v where unwrap :: w -> v
data Boxed w v = Boxed { unwrap :: w -> v }

-- instance Bool String where unwrap = ...
boxedBoolString = Boxed unwrap
  where
    unwrap = ... -- конкретная реализация

-- let { x :: String ; x = unwrap True }
let x = unwrap boxedBoolString True


К>А С++ные шаблонные функции — это, как бы, макросы.

К>Программист спрятал определение -> компилятор не смог его инстанцировать для произвольного заданного типа -> компилятор решил, что программист где-то это инстанцирование сделал руками -> компилятор оставил заявку линкеру -> линкер выругался.

Т.е. разница в том, что в Haskell мы не можем дать объявление функции без определения? Или что?
Re[6]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 17.07.10 12:33
Оценка:
Здравствуйте, lomeo, Вы писали:

L>C Haskell в чём проблема разнести на модули? (Я просто не пойму что требуется


Требуется сделать так, чтобы main создал структуру Box$Box$Box$Box$123 и с минимумом усилий отдал её в функцию unwrap.
А пока что получается, что main должен сам родить все промежуточные инстансы Boxed с их unwrap.
Или я чего-то не понял.

L>>> Насчёт вытаскивания определения функций на божий свет не понял — что имеется в виду?


Аналогично inline в C|C++
(Объявление — это имя плюс тип. Определение — это потроха функции).

К>>В хаскелле, как и в С++, можно определения функций давать прямо в точке объявления

К>>а можно вынести в объявление модуля, сделав определение непрозрачным для пользователей. (Как это сделали, например, с монадой IO).

L>Всё равно не понял — IO не функция. Что значит "определение функции непрозрачно для пользователя"?


IO — это букет из функций: конструктор данных и инстанс монады, плюс IO-специфичные функции ввода-вывода-мутабельности.
Наружу видны только объявления (причём конструктор недоступен). О паттерн-матчинге тоже речи нет.
Вся реализация скомпилирована и спрятана, ибо.


К>>Но хаскелловские полиморфные функции — это, как бы, дженерики. На входе void* плюс словари, на выходе void* плюс словари. И внутри коммутация между входами и выходами словарных функций. То есть, определение можно спокойно прятать.


L>Никаких словарных функций нет, на выходе тоже словарей нет.


Это из той же серии "никаких vtbl и vfptr нет, виртуальные вызовы — это колдовство компилятора, как он хочет, так и реализует".
Предназначено для выбивания дури из новичков, чтобы их шаловливые руки не тянулись к *(void**)this

L>Использование unwrap :: (Boxed w v) => w -> v генерится во что-то вроде


L>
L>-- class Boxed w v where unwrap :: w -> v
L>data Boxed w v = Boxed { unwrap :: w -> v }

L>-- instance Bool String where unwrap = ...
L>boxedBoolString = Boxed unwrap
L>  where
L>    unwrap = ... -- конкретная реализация

L>-- let { x :: String ; x = unwrap True }
L>let x = unwrap boxedBoolString True
L>


Ну и что такое, по твоему, boxedBoolString? Словарь и есть

А что генерится не для конкретного instance Boxed Bool String, а для параметризованного — instance (Boxed b v) => Boxed (Box b) v ?
Что-то у меня голова не соображает.

Причём, клиентский код подсовывает нужный словарь: unwrap boxedBoolString
А если там Box$Box$Box$True — это надо подсунуть словарь boxedBoxBoxBoxBoolString, правильно?


К>>А С++ные шаблонные функции — это, как бы, макросы.

К>>Программист спрятал определение -> компилятор не смог его инстанцировать для произвольного заданного типа -> компилятор решил, что программист где-то это инстанцирование сделал руками -> компилятор оставил заявку линкеру -> линкер выругался.

L>Т.е. разница в том, что в Haskell мы не можем дать объявление функции без определения? Или что?


Разница в том, что в Haskell как раз мы можем дать объявление полиморфной функции без определения. Яркий пример тому — всё тот же IO.
А в С++ шаблонная функция должна быть или полностью на виду, или программист должен нарожать конкретных экземпляров с полностью подставленными параметрами. Близкий аналог — это С++ные интерфейсы, но там полиморфизм куда более бедный и скованный, чем у хаскелла или ML
Перекуём баги на фичи!
Re[7]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 17.07.10 14:07
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, lomeo, Вы писали:


К>Требуется сделать так, чтобы main создал структуру Box$Box$Box$Box$123 и с минимумом усилий отдал её в функцию unwrap.

К>А пока что получается, что main должен сам родить все промежуточные инстансы Boxed с их unwrap.
К>Или я чего-то не понял.

Инстансы — это словари. Они уже есть, main их не рожает. Получается, что он просто при выводе типов знает что использовать. Допустим, в случае inline родится (unwrap boxedBox (unwrap boxedBox ... (unwrap boxedAny (Box (Box ... (Box 5)))))), где boxedBox и boxedAny — словари. Заранее определённые. Но так со всеми функциями в Хаскель! Мы можем вынести из main с помощью какого-нибудь data Box v = forall w. Box w (w -> v), но не больше.

К>IO — это букет из функций: конструктор данных и инстанс монады, плюс IO-специфичные функции ввода-вывода-мутабельности.

К>Наружу видны только объявления (причём конструктор недоступен). О паттерн-матчинге тоже речи нет.
К>Вся реализация скомпилирована и спрятана, ибо.

Модулями спрятана же. Мы можем тоже сделать это с помощью smart constructors, rankNTypes и прочей магии. Другое дело, что динамику мы можем сделать только с помощью cast (напрямую или через библиотеки — Dynamic, SYB, Uniplate):

> head [v | (v :: Int) <- universeBi $ Box $ Box $ Box $ Box $ Box (42 :: Int)]
42


L>>Никаких словарных функций нет, на выходе тоже словарей нет.


К>Это из той же серии "никаких vtbl и vfptr нет, виртуальные вызовы — это колдовство компилятора, как он хочет, так и реализует".

К>Предназначено для выбивания дури из новичков, чтобы их шаловливые руки не тянулись к *(void**)this

Где на выходе словари?

К>Ну и что такое, по твоему, boxedBoolString? Словарь и есть


Ну да. Меня смутило то, что ты говоришь "на выходе".

К>А что генерится не для конкретного instance Boxed Bool String, а для параметризованного — instance (Boxed b v) => Boxed (Box b) v ?

К>Что-то у меня голова не соображает.

boxedBoxAny boxedDict (Box boxed) = (unwrap boxedDict) boxed


и используется не boxedBoxAny, а boxedBoxAny concreateBoxedDict

К>Причём, клиентский код подсовывает нужный словарь: unwrap boxedBoolString

К>А если там Box$Box$Box$True — это надо подсунуть словарь boxedBoxBoxBoxBoolString, правильно?

boxedBoxAny (boxedBoxAny (boxedBoxAny boxedBoolString))


К>Разница в том, что в Haskell как раз мы можем дать объявление полиморфной функции без определения. Яркий пример тому — всё тот же IO.

К>А в С++ шаблонная функция должна быть или полностью на виду, или программист должен нарожать конкретных экземпляров с полностью подставленными параметрами. Близкий аналог — это С++ные интерфейсы, но там полиморфизм куда более бедный и скованный, чем у хаскелла или ML

Мне кажется, не можем мы дать объявление полиморфной функции без определения. Посмотри на реализацию IO, там обычные функции с определениями, которые зовут foreign functions (stdlib какой-нибудь).
Re[8]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 17.07.10 18:20
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Инстансы — это словари. Они уже есть, main их не рожает. Получается, что он просто при выводе типов знает что использовать. Допустим, в случае inline родится (unwrap boxedBox (unwrap boxedBox ... (unwrap boxedAny (Box (Box ... (Box 5)))))), где boxedBox и boxedAny — словари. Заранее определённые. Но так со всеми функциями в Хаскель! Мы можем вынести из main с помощью какого-нибудь data Box v = forall w. Box w (w -> v), но не больше.


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


К>>IO — это букет из функций: конструктор данных и инстанс монады, плюс IO-специфичные функции ввода-вывода-мутабельности.

К>>Наружу видны только объявления (причём конструктор недоступен). О паттерн-матчинге тоже речи нет.
К>>Вся реализация скомпилирована и спрятана, ибо.

L>Модулями спрятана же.


Я про это и говорю. А в С++ попробуй спрячь реализацию макроса... с вероятностью 99.99% огребёшь от линкера (ибо компилятор позволяет прятать, полагая, что программист знает, что делает).


L>Где на выходе словари?


Возможно, в этом месте на меня нашло лунное затмение. Я даже знаю, почему:
void foo(IX*);
IY* bar(IX*);
void buz(IY*);

void foo(IX* x) // полиморфная функция, принимает интерфейс (словарь класса подмонтирован к объекту)
{
  IY* y = bar(y); // фиг его знает, что там делает bar и какой фактический тип вернёт...
  buz(y);           // но словарь класса Y подмонтирован к объекту, поэтому нам настаёт щасте
}

На хаскелле этому соответствует... я даже затрудняюсь сказать, что.



К>>Разница в том, что в Haskell как раз мы можем дать объявление полиморфной функции без определения. Яркий пример тому — всё тот же IO.

К>>А в С++ шаблонная функция должна быть или полностью на виду, или программист должен нарожать конкретных экземпляров с полностью подставленными параметрами. Близкий аналог — это С++ные интерфейсы, но там полиморфизм куда более бедный и скованный, чем у хаскелла или ML

L>Мне кажется, не можем мы дать объявление полиморфной функции без определения. Посмотри на реализацию IO, там обычные функции с определениями, которые зовут foreign functions (stdlib какой-нибудь).


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

Кстати, о "посмотри на реализацию". Полез в дистрибутив ghc 6.10.1, — там ни одного исходника на хаскелле нет, всё уже скомпилировано.
Чёрт, где же я раньше мог видеть содержимое Прелюдии?... то ли в древнем ghc, то ли в hugs.
Перекуём баги на фичи!
Re[9]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 18.07.10 05:26
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Меня просто интересовало, что делать с многопараметрическими классами.

К>Наверно, клиентский код пишет мега-комбинацию из готовых словарей.

Неа, один инстанс — один словарь.

L>>Модулями спрятана же.

К>Я про это и говорю. А в С++ попробуй спрячь реализацию макроса... с вероятностью 99.99% огребёшь от линкера (ибо компилятор позволяет прятать, полагая, что программист знает, что делает).

Ага, теперь понял, спасибо.

L>>Где на выходе словари?

К>Возможно, в этом месте на меня нашло лунное затмение. Я даже знаю, почему:
К>
К>void foo(IX*);
К>IY* bar(IX*);
К>void buz(IY*);

К>void foo(IX* x) // полиморфная функция, принимает интерфейс (словарь класса подмонтирован к объекту)
К>{
К>  IY* y = bar(y); // фиг его знает, что там делает bar и какой фактический тип вернёт...
К>  buz(y);           // но словарь класса Y подмонтирован к объекту, поэтому нам настаёт щасте
К>}
К>

К>На хаскелле этому соответствует... я даже затрудняюсь сказать, что.

Обычные однопараметрические классы? Что-то лучше от объяснения не стало, я ещё больше запутался

К>Почему это не можем. Пускай у линкера голова болит, искать определения в объектных библиотеках? Имя есть, сигнатура есть, всё, что нужно нам для вызова.


Нужна именно возможность ситуации, когда линкер не найдёт определения в объектниках?

К>Кстати, о "посмотри на реализацию". Полез в дистрибутив ghc 6.10.1, — там ни одного исходника на хаскелле нет, всё уже скомпилировано.

К>Чёрт, где же я раньше мог видеть содержимое Прелюдии?... то ли в древнем ghc, то ли в hugs.

Сырцы отдельно качаются http://www.haskell.org/ghc/download_ghc_6_12_2.html#sources в самом низу.
Re[10]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 18.07.10 09:33
Оценка:
Здравствуйте, lomeo, Вы писали:

L>>>Где на выходе словари?

К>>Возможно, в этом месте на меня нашло лунное затмение. Я даже знаю, почему:
К>>
К>>void foo(IX*);
К>>IY* bar(IX*);
К>>void buz(IY*);

К>>void foo(IX* x) // полиморфная функция, принимает интерфейс (словарь класса подмонтирован к объекту)
К>>{
К>>  IY* y = bar(y); // фиг его знает, что там делает bar и какой фактический тип вернёт...
К>>  buz(y);           // но словарь класса Y подмонтирован к объекту, поэтому нам настаёт щасте
К>>}
К>>

К>>На хаскелле этому соответствует... я даже затрудняюсь сказать, что.

L>Обычные однопараметрические классы? Что-то лучше от объяснения не стало, я ещё больше запутался


Не совсем. Ибо, bar() может вернуть любой из Y1, Y2, Y3 для одного и того же входного типа X1.
Это, скорее, экзистенциальные типы.
class IY y where buz :: y -> String
class IX x where bar :: forall y . IY y => x -> y

foo :: IX x => x -> IO ()
foo x = do
  let y = bar x
  let s = buz y
  putStrLn "hello, " ++ s

data Y1 = Y1
instance IY Y1 where buz = "one"

data Y2 = Y2 s
instance IY Y2 where buz = "two " ++ s

data X = forall y . IY y => X y
instance IX x where bar (X y) = y
instance IX x => IX [x] where bar = bar . hd

main = do
  let x1 = [X Y1]
  let x2 = [X (Y2 "test")]
  foo x1
  foo x2
  foo (x1++x2)
Перекуём баги на фичи!
Re[11]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 18.07.10 15:42
Оценка:
Здравствуйте, Кодт, Вы писали:

L>>Обычные однопараметрические классы? Что-то лучше от объяснения не стало, я ещё больше запутался

К>Не совсем. Ибо, bar() может вернуть любой из Y1, Y2, Y3 для одного и того же входного типа X1.

Ну да, возьми, например, тип read Правда, тогда нельзя будет использовать foo (x1++x2) из твоего примера, но с исходным посылом проблем нет.

class IY y where buz :: y -> String
class IY y => IX x y where bar :: x -> y


Ну, а дальше просто

data Y1 = Y1
instance IY Y1 where buz _ = "one"

data Y2 = Y2 String
instance IY Y2 where buz (Y2 s) = "two " ++ s

data X y = X y
instance (IY y, y ~ r) => IX (X y) r where bar (X y) = y
instance (IX x y, IY y) => IX [x] y where bar = bar . head

foo x = do
  let y = bar x
  let s = buz y
  putStrLn $ "hello, " ++ s

main = do
  let x1 = [X Y1]
  let x2 = [X (Y2 "test")]
  foo x1
  foo x2


К>Это, скорее, экзистенциальные типы.

К>class IX x where bar :: forall y . IY y => x -> y


Так нельзя — у тебя же на выходе экзистенциальный тип, словарь дальше нельзя будет передать. Попробуй сам скомпилировать свой пример — поймёшь, что я имею в виду.
Re[12]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 18.07.10 20:47
Оценка:
Здравствуйте, lomeo, Вы писали:

L>>>Обычные однопараметрические классы? Что-то лучше от объяснения не стало, я ещё больше запутался

К>>Не совсем. Ибо, bar() может вернуть любой из Y1, Y2, Y3 для одного и того же входного типа X1.

L>Ну да, возьми, например, тип read Правда, тогда нельзя будет использовать foo (x1++x2) из твоего примера, но с исходным посылом проблем нет.


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

L>
L>class IY y where buz :: y -> String
L>class IY y => IX x y where bar :: x -> y
L>

<>
Это нечестно! Таким образом мы передадим в foo аргументы разных известных типов — [X Y1] и [X Y2], вместе с разными инстансами классов IX и IY. И закономерно получим разные результаты

К>>Это, скорее, экзистенциальные типы.

L>
К>>class IX x where bar :: forall y . IY y => x -> y
L>

L>Так нельзя — у тебя же на выходе экзистенциальный тип, словарь дальше нельзя будет передать. Попробуй сам скомпилировать свой пример — поймёшь, что я имею в виду.

А на С++ можно
Кстати, как на хаскелле из этого выкручиваются? Я ещё не шибко овладел хаск-фу.
Перекуём баги на фичи!
Re[13]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 19.07.10 10:56
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А на С++ можно

К>Кстати, как на хаскелле из этого выкручиваются? Я ещё не шибко овладел хаск-фу.

Никак Строгая типизация. Но если очень надо, то всякие Dynamic и прочее.
Re[14]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 19.07.10 13:38
Оценка:
Здравствуйте, lomeo, Вы писали:

К>>Кстати, как на хаскелле из этого выкручиваются? Я ещё не шибко овладел хаск-фу.

L>Никак Строгая типизация. Но если очень надо, то всякие Dynamic и прочее.

Ну вот смотри
{-# LANGUAGE ExistentialQuantification #-}

data X = forall y . Show y => X y -- возьмём уже готовый класс, чтобы сразу и наглядно

instance Show X where -- и X тоже в этот класс поместим, чтобы не возиться
  show (X y) = "X $ " ++ show y

-- автоматически получили instance Show [X]

main = putStrLn $ show [X 1, X "hello", X $ X [X 123]] -- нагородили чёрте что, но это работает!

Так можно. (Ясное дело, что X тащит внутри себя словарь инстанса Show для конкретного значения).

А вот так
instance Show X where
  show x = "X $ " ++ show (getY x)

Уже фиг.
За пределами определения data X мы просто не сможем определить getY, а если это селектор записи, то от него никакого толку.

Только если мы протащим show внутрь
showY :: X -> String
showY (X y) = show y

instance Show X where
  show x = "X $ " ++ showY x

или, в общем виде (с опцией -XRank2Types)
applyToY :: (forall y . Show y => y -> String) -> X -> String
applyToY f (X y) = f y

instance Show X where
  show x = "X $ " ++ applyToY show x


Почему мир так жесток?
Перекуём баги на фичи!
Re[15]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 19.07.10 13:56
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Почему мир так жесток?


Экзистенциальные типы — "забытые" типы. В контексте Хаскеля это означает, что словарь забывается тоже

Соотвественно, вытащить "forall a. ... -> a" ты можешь только с чем-нибудь, что работает с этим "a" вместе. Иначе ты просто не сможешь его обработать дальше.

http://newstar.rinet.ru/~goga/tapl/tapl029.html — почитай про применение в 24.2

Если ты, работая с экзистенциальными типами, помнишь, что их надо упаковывать с операциями (потому что они забываются, и надо, чтобы о них что-то помнило), то увидишь, что твой пример с bar/baz можно написать только впихнув обработку IY (кажется bar, не помню) вместе с forall y. IY y => y в одно значение

data X = forall IY y. X y (y -> IO ())


Если же тебе нужно обязательно иметь и первое и второе — обычный ADT тебя спасёт. Если он не очень расширябелен для твоей задачи — можно погуглить про решение expression problem в Haskell (ищи Datatypes a la Carte). Ну или, как я уже говорил, cast — опускаемся до уровня C++

Вот вывернутый наизнаку пример http://lomeo.livejournal.com/45273.html
Re[16]: Этюд Unbox(Box<Box<...Box<T>...>> )
От: Кодт Россия  
Дата: 19.07.10 21:24
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Если же тебе нужно обязательно иметь и первое и второе — обычный ADT тебя спасёт. Если он не очень расширябелен для твоей задачи — можно погуглить про решение expression problem в Haskell (ищи Datatypes a la Carte). Ну или, как я уже говорил, cast — опускаемся до уровня C++


Кажется, я понял фикус. Нужно не только класс IY написать, но и мономорфный контейнер PtrY — то, что в С++ соответствует указателю на базовый тип.
class IY y where
  buz :: y -> IO ()

data PtrY = forall y . IY y => PtrY y
instance IY PtrY where
  buz (PtrY y) = buz y


class IX x where
  bar :: x -> PtrY


Подозреваю, что это идиоматическое выражение, годное для любого класса...
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.