До 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 непредсказуемо!
- В Haskell 1.3 используется
-
Имена менее общие (например,
map
vs.fmap
). Не большая проблема, но это шип в моих глазах.
В целом, я думаю, что эти изменения были не самыми лучшими. На самом деле, я думаю, что они были шагом назад от Haskell 1.4. Почему эти вещи изменились для Haskell 98 и почему так?
В стороне, я могу представить себе следующую защиту:
- "
fail
позволяет обнаруживать ошибки". Только для программистов и только во время выполнения. Сообщение об ошибке (unportable!) - это не то, что вы хотите проанализировать. Если вы действительно заботитесь об этом, вы должны отслеживать это явно. Теперь у нас естьControl.Failure
из пакетаfailure
, который намного лучше работает (failure x
ведет себя в основном какzero
). - "Слишком много классов делает разработку и использует слишком много". Слишком мало классов нарушает их законы, и эти законы так же важны, как и типы.
- "Функции с ограниченным доступом легче изучить". Тогда почему нет
SimplePrelude
, а большинство классов удалено? Это только одна магическая декларация для студентов, они могут так много обойтись. (Возможно, требуется и{-# LANGUAGE RebindableSyntax #-}
, но опять же, студенты очень хорошо разбираются в копировании). - "Ограниченные экземпляром функции делают ошибки более читабельными". Я использую
fmap
гораздо чаще, чемmap
, так почему бы неmap
иlistMap
вместо?