Ассоциированные типы и элементы контейнеров

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

Haskell - фантастика в работе с параметрическим полиморфизмом. Но проблема в том, что не все является параметрическим. В качестве тривиального примера предположим, что мы хотим получить первый элемент данных из контейнера. Для параметрического типа это тривиально:

class HasFirst c where
  first :: c x -> Maybe x

instance HasFirst [] where
  first []    = Nothing
  first (x:_) = Just x

Теперь попробуйте написать экземпляр для ByteString. Не получится. Его тип не упоминает тип элемента. Вы также не можете написать экземпляр для Set, потому что он требует ограничения Ord - но глава класса не упоминает тип элемента, поэтому вы не можете его ограничить.

Ассоциированные типы предоставляют изящный способ полностью устранить эти проблемы:

class HasFirst c where
  type Element c :: *
  first :: c -> Maybe (Element c)

instance HasFirst [x] where
  type Element [x] = x
  first []    = Nothing
  first (x:_) = Just x

instance HasFirst ByteString where
  type Element ByteString = Word8
  first b = b ! 0

instance Ord x => HasFirst (Set x) where
  type Element (Set x) = x
  first s = findMin s

Однако теперь у нас появилась новая проблема. Рассмотрим попытку "исправить" Functor так, чтобы он работал для всех типов контейнеров:

class Functor f where
  type Element f :: *
  fmap :: (Functor f2) => (Element f -> Element f2) -> f -> f2

Это совсем не работает. Здесь говорится, что если у нас есть функция от типа элемента f к типу элемента f2, то мы можем превратить f в f2. Пока все хорошо. Однако, очевидно, нет никакого способа потребовать, чтобы f и f2 были одним и тем же видом контейнера!

Согласно существующему определению Functor, мы имеем

fmap :: (x -> y) -> [x] -> [y]
fmap :: (x -> y) -> Seq x -> Seq y
fmap :: (x -> y) -> IO x -> IO y

Но мы не имеем fmap :: (x -> y) -> IO x -> [y]. Это совершенно невозможно. Но приведенное выше определение класса позволяет это сделать.

Кто-нибудь знает, как объяснить системе типов, что я на самом деле имел в виду?

Edit

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

21
задан MathematicalOrchid 26 January 2012 в 12:49
поделиться