length - funcion filter en haskell
¿Por qué las clases estándar de Haskell 98 fueron inferiores a las de Haskell 1.3? (1)
¿Por qué se cambiaron estas cosas para Haskell 98 y por qué de esta manera?
Haskell 98 implica mucha simplificación del lenguaje (gran parte de la cual ya se ha revertido). El objetivo era mejorar Haskell como lenguaje de enseñanza y tomar decisiones relativamente conservadoras.
Ver por ejemplo
Consideramos Haskell 98 como un diseño razonablemente conservador. Por ejemplo, en ese momento las clases de tipos de parámetros múltiples estaban siendo ampliamente utilizadas, pero Haskell 98 solo tiene clases de tipos de parámetros únicos (Peyton Jones et al., 1997).
Y:
Haskell 98 no será de ninguna manera la última revisión de Haskell. Por el contrario, lo diseñamos sabiendo que las nuevas extensiones de lenguaje (clases de tipo de múltiples parámetros, cuantificación universal y existencial, protectores de patrones, etc, etc.) están en camino. Sin embargo, Haskell 98 tendrá un estado especial: la intención es que los compiladores de Haskell continúen admitiendo Haskell 98 (dado un indicador apropiado) incluso después de que se hayan definido versiones posteriores del lenguaje, por lo que el nombre "Haskell 98" se referirá a Un lenguaje fijo, estable.
Entonces, las cosas se simplificaron, con el objetivo de producir un estándar más simple.
Antes de Haskell 98, había Haskell 1.0 a 1.4. Es bastante interesante ver el desarrollo a lo largo de los años, ya que las funciones se agregaron a las primeras versiones de Haskell estandarizado.
Por ejemplo, la notación fue estandarizada por primera vez por Haskell 1.3 (publicada 1996-05-01). En el Prelude
, encontramos las siguientes definiciones (página 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
Las mismas definiciones se encuentran en Haskell 1.4. Tengo algunos problemas con esto (por ejemplo, la reforma de MonadPlus
no ha ocurrido aquí todavía), pero en general, es una muy buena definición.
Esto es muy diferente de Haskell 98, donde se encuentra la siguiente definición:
-- 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
Esta es también la definición en Haskell 2010. Tengo los siguientes problemas con esta definición:
-
MonadZero
yMonadPlus
se han ido. Eran clases útiles. En caso de una falla de coincidencia de patrón en una do-notación ...
- Haskell 1.3 usa
zero
. Se aplica la ley del Cero Izquierdo (zero >>= k = zero
), para que sepa qué se supone que suceda. - Haskell 98 usa
fail msg
, dondemsg
es generado por el compilador en el caso de GHC. Cualquier cosa puede pasar, no hay garantías sobre su semántica. Por lo tanto, no es una gran función para los usuarios. Como consecuencia, el comportamiento de los fallos de coincidencia de patrón en la notación de Haskell 98 es impredecible.
- Haskell 1.3 usa
Los nombres son menos generales (por ejemplo,
map
vs.fmap
). No es un gran problema, pero es una espina en mi ojo.
Con todo, creo que estos cambios no fueron para mejor. De hecho, creo que fueron un paso atrás de Haskell 1.4. ¿Por qué se cambiaron estas cosas para Haskell 98 y por qué de esta manera?
A un lado, puedo imaginar las siguientes defensas:
- "
fail
permite localizar errores". Solo para programadores, y solo en tiempo de ejecución. El mensaje de error (¡unportable!) No es exactamente algo que desee analizar. Si realmente te importa, deberías seguirlo explícitamente. Ahora tenemosControl.Failure
del paquete defailure
, que hace un trabajo mucho mejor en esto (elfailure x
comporta principalmente comozero
). - "Tener demasiadas clases hace que el desarrollo y el uso sean demasiado difíciles". Tener muy pocas clases infringe sus leyes, y esas leyes son tan importantes como los tipos.
- "Las funciones restringidas a instancias son más fáciles de aprender". Entonces, ¿por qué no hay un
SimplePrelude
enSimplePrelude
lugar, con la mayoría de las clases eliminadas? Es solo una declaración mágica para los estudiantes, ellos pueden manejar eso. (Quizás también se necesita{-# LANGUAGE RebindableSyntax #-}
, pero nuevamente, los estudiantes son muy buenos para copiar y pegar cosas). - "Las funciones restringidas de instancia hacen que los errores sean más legibles". Uso
fmap
mucho más a menudo quemap
, así que, ¿por qué nomap
ylistMap
enlistMap
lugar?