Почему стандартные классы Haskell 98 уступают Haskell 1.3?
До Haskell 98 были Haskell с 1.0 по 1.4. Очень интересно видеть развитие на протяжении многих лет, поскольку функции были добавлены к ранним версиям стандартизованного Haskell.
Например, предварительная нотация была сначала стандартизирована Haskell 1.3 (опубликовано в 1996-05-01). В Prelude
мы находим следующие определения (стр. 87):
-- Monadic classes
class Functor f where
map :: (a -> b) -> f a -> f b
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
m >> k = m >>= \_ -> k
class (Monad m) => MonadZero m where
zero :: m a
class (MonadZero m) => MonadPlus m where
(++) :: m a -> m a -> m a
Те же определения найдены в Haskell 1.4. У меня есть несколько проблем с этим (например, MonadPlus
reform здесь еще не произошло), но в целом, это очень приятно определение.
Это сильно отличается от Haskell 98, где найдено следующее определение:
-- Monadic classes
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
-- Minimal complete definition:
-- (>>=), return
m >> k = m >>= \_ -> k
fail s = error s
Это также определение в Haskell 2010. У меня есть следующие проблемы с этим определением:
-
MonadZero
и MonadPlus
исчезли. Они были полезными классами.
-
В случае неудачи совпадения шаблона в do-notation...
- В Haskell 1.3 используется
zero
. Закон Левого нуля применяется (zero >>= k = zero
), поэтому вы знаете, что должно произойти.
- Haskell 98 использует
fail msg
, где msg
генерируется компилятором в случае GHC. Все может случиться, никаких гарантий относительно его семантики. Поэтому он не очень удобен для пользователей. Как следствие, поведение неудачных совпадений шаблонов в служебной записи Haskell 98 непредсказуемо!
-
Имена менее общие (например, map
vs. fmap
). Не большая проблема, но это шип в моих глазах.
В целом, я думаю, что эти изменения были не самыми лучшими. На самом деле, я думаю, что они были шагом назад от Haskell 1.4. Почему эти вещи изменились для Haskell 98 и почему так?
В стороне, я могу представить себе следующую защиту:
- "
fail
позволяет обнаруживать ошибки". Только для программистов и только во время выполнения. Сообщение об ошибке (unportable!) - это не то, что вы хотите проанализировать. Если вы действительно заботитесь об этом, вы должны отслеживать это явно. Теперь у нас есть Control.Failure
из пакета failure
, который намного лучше работает (failure x
ведет себя в основном как zero
).
- "Слишком много классов делает разработку и использует слишком много". Слишком мало классов нарушает их законы, и эти законы так же важны, как и типы.
- "Функции с ограниченным доступом легче изучить". Тогда почему нет
SimplePrelude
, а большинство классов удалено? Это только одна магическая декларация для студентов, они могут так много обойтись. (Возможно, требуется и {-# LANGUAGE RebindableSyntax #-}
, но опять же, студенты очень хорошо разбираются в копировании).
- "Ограниченные экземпляром функции делают ошибки более читабельными". Я использую
fmap
гораздо чаще, чем map
, так почему бы не map
и listMap
вместо?
Ответы
Ответ 1
Почему эти вещи изменились для Haskell 98 и почему так?
Haskell 98 включает в себя много упрощения языка (большая часть которого с тех пор была отменена). Цель состояла в том, чтобы улучшить Haskell в качестве учебного языка и сделать относительно консервативный выбор.
См., например,
Мы рассматривали Haskell 98 как достаточно консервативный дизайн. Для Например, к этому времени многопараметрические типы классов широко, но Haskell 98 имеет только классы с одним параметром (Peyton Jones et al., 1997).
В: История Haskell
и
Haskell 98 ни в коем случае не будет последней версией Haskell. На напротив, мы разрабатываем его, зная, что новые языковые расширения (классы с несколькими параметрами, универсальные и экзистенциальные количественная оценка, ограждения рисунков и т.д. и т.д.). Однако Haskell 98 будет иметь особый статус: предполагается, что Компиляторы Haskell будут продолжать поддерживать Haskell 98 (учитывая соответствующий флаг) даже после того, как более поздние версии языка были и поэтому имя "Haskell 98" будет ссылаться на фиксированную, стабильную язык.
В: отчет Haskell98
Итак, все упростилось, с целью создания более простого стандарта.