Я думаю, что я мог бы спросить об этом в 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]
. Это совершенно невозможно. Но определение класса выше позволяет это.
Кто-нибудь знает, как объяснить системе типа, что я имел в виду ?
Редактировать
Вышеуказанные работы по определению способ вычисления типа элемента от типа контейнера. Что произойдет, если вы попытаетесь сделать это наоборот? Определить функцию для вычисления типа контейнера из типа элемента? Легко ли это получается?
Все еще изо всех сил пытаюсь понять, что здесь происходит ... Логично, это просто. Мы хотим, чтобы функция карты принимала какой-либо законный тип ввода и выдавала какой-либо законный тип вывода. Трудная часть определяет, какие типы являются «законными» для данного контейнера. (Я не во все это «жесткое» и «параметрическое» различие.) Может ли кто-нибудь объяснить, что означают все символы «~»? Или что означает «ограничение»? – MathematicalOrchid
Я расширил свой ответ, надеюсь, лучше объяснить ситуацию; Я бы также рекомендовал прочитать сообщение в блоге, которое я связал, для более подробного объяснения 'Constraint'. – ehird
Так что добрый «*» - это обычный тип, вид «* -> *» - это любой конструктор типа, а вид «Constraint» не является типом вообще, это ограничение типа? Это правильно? – MathematicalOrchid