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...
Пока на собственное сообщение не было ответов, его можно удалить.