Думаю, я мог бы спросить об этом в Haskell-Cafe в какой-то момент, но, черт возьми, если я смогу найти ответ сейчас... Так что я снова прошу об этом здесь, поэтому, надеюсь, в будущем я смогу найти ответ!
Хаскелл фантастичен в борьбе с параметрическим полиморфизмом. Но проблема в том, что не все параметрическое. В качестве тривиального примера предположим, что мы хотим извлечь первый элемент данных из контейнера. Для параметрического типа это тривиально:
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
Вышеописанное работает, определяя способ вычисления типа элемента из типа контейнера. Что произойдет, если вы попытаетесь сделать это наоборот? Определить функцию для вычисления типа контейнера из типа элемента? Легко ли это работает?