haskell coding-style warnings compiler-warnings ghc

haskell - Impacto en el estilo de GHC-Wall



coding-style warnings (6)

Creo que el uso de -Wall puede llevar a un código menos legible. Sobre todo, si se está haciendo alguna aritmética.

Algunos otros ejemplos, donde el uso de -Wall sugiere modificaciones con una legibilidad peor.

(^) con -Wall requiere firmas de tipo para exponentes

Considere este código:

norm2 x y = sqrt (x^2 + y^2) main = print $ norm2 1 1

Con -Wall da dos advertencias como esta:

rt.hs:1:18: Warning: Defaulting the following constraint(s) to type `Integer'' `Integral t'' arising from a use of `^'' at rt.hs:2:18-20 In the first argument of `(+)'', namely `x ^ 2'' In the first argument of `sqrt'', namely `(x ^ 2 + y ^ 2)'' In the expression: sqrt (x ^ 2 + y ^ 2)

Escribir (^(2::Int) todas partes en lugar de (^2) no es agradable.

Se requieren firmas de tipo para todos los niveles superiores

Al escribir código rápido y sucio, es molesto. Para el código simple, donde hay como máximo uno o dos tipos de datos en uso (por ejemplo, sé que trabajo solo con Double s), escribir firmas en todas partes puede complicar la lectura. En el ejemplo anterior hay dos advertencias solo por la falta de firma de tipo:

rt.hs:1:0: Warning: Definition but no type signature for `norm2'' Inferred type: norm2 :: forall a. (Floating a) => a -> a -> a ... rt.hs:2:15: Warning: Defaulting the following constraint(s) to type `Double'' `Floating a'' arising from a use of `norm2'' at rt.hs:2:15-23 In the second argument of `($)'', namely `norm2 1 1'' In the expression: print $ norm2 1 1 In the definition of `main'': main = print $ norm2 1 1

Como una distracción, uno de ellos se refiere a la línea diferente de la que se necesita la firma de tipo.

Tipo de firmas para cálculos intermedios con Integral son necesarios.

Este es un caso general del primer problema. Considere un ejemplo:

stripe x = fromIntegral . round $ x - (fromIntegral (floor x)) main = mapM_ (print . stripe) [0,0.1..2.0]

Da un montón de advertencias. En todas partes con fromIntegral para convertir de nuevo a Double :

rt2.hs:1:11: Warning: Defaulting the following constraint(s) to type `Integer'' `Integral b'' arising from a use of `fromIntegral'' at rt2.hs:1:11-22 In the first argument of `(.)'', namely `fromIntegral'' In the first argument of `($)'', namely `fromIntegral . round'' In the expression: fromIntegral . round $ x - (fromIntegral (floor x))

Y todo el mundo sabe con qué frecuencia se necesita desde fromIntegral en Haskell ...

Hay más casos como estos, los riesgos del código numérico se vuelven ilegibles solo para cumplir con los requisitos de -Wall . Pero sigo usando -Wall en el código del que me gustaría estar seguro.

Se considera una buena práctica habilitar las advertencias de GHC con -Wall . Sin embargo, he descubierto que corregir esas advertencias tiene un efecto negativo en algunos tipos de construcciones de código.

Ejemplo 1:

El uso de la do-notación equivalente de f >> generará una advertencia si no utilizo explícitamente el formulario _ <- f :

Warning: A do-notation statement discarded a result of type Char. Suppress this warning by saying "_ <- f", or by using the flag -fno-warn-unused-do-bind

Entiendo que puedo olvidarme de hacer algo con el resultado de f . Sin embargo, es legítimo ignorar el resultado (muy común en los analizadores). No hay advertencia cuando se usa >> , ¿verdad? Usar _ <- es más pesado de lo que debería.

Ejemplo 2:

Nombrar una variable de patrón con el mismo nombre de una función visible dará:

Warning: This binding for `map'' shadows the existing binding imported from Prelude

Esto está empeorando cuando se usa la sintaxis de registro ya que el espacio de nombres se contamina rápidamente. La solución es dar un nombre alternativo en la expresión de patrón. Así que termino usando un nombre menos apropiado solo para evitar una advertencia. No creo que sea una razón suficientemente buena.

Sé que puedo usar las -fno-warn-... pero debo seguir con -Wall después de todo?


El sombreado de nombres puede ser bastante peligroso. En particular, puede ser difícil razonar sobre el alcance de un nombre que se introduce.

Los enlaces de patrones no utilizados en la notación do no son tan malos, pero pueden indicar que se está utilizando una función menos eficiente de la necesaria (por ejemplo, mapM lugar de mapM_ ).

Como señaló BenMos, usar void o ignore para descartar explícitamente los valores no utilizados es una buena manera de ser explícito sobre las cosas.

Sería bastante bueno poder deshabilitar las advertencias para solo una sección de código, en lugar de para todo a la vez. Además, las banderas cabal y las líneas ghc de línea de comando tienen prioridad sobre las banderas en un archivo, por lo que no puedo tener -Wall por defecto en todas partes e incluso simplemente deshabilitarlo para la totalidad de un solo archivo.


Recomendaría seguir usando ''-Wall'' como la opción predeterminada, y deshabilitar las comprobaciones que necesite en forma local, por módulo, utilizando un pragma OPTIONS_GHC en la parte superior de los archivos relevantes.

La única razón por la que podría hacer una excepción es ''-fno-warn-unsused-do-bind'', pero una sugerencia podría ser usar una función explícita ''void'' ... escribir ''void f'' parece mejor que ''_ < - f ''.

En cuanto al nombre de sombreado, creo que en general es bueno evitarlo si se puede, ya que ver "mapa" en medio de algún código hará que la mayoría de los Haskellers esperen la biblioteca estándar fn.


También existe la opción -W mucho menos intrusiva, que permite un conjunto de advertencias razonables relacionadas principalmente con el estilo de codificación general (importaciones no utilizadas, variables no utilizadas, coincidencias de patrones incompletas, etc.).

En particular, no incluye las dos advertencias que mencionó.


Todas estas advertencias ayudan a prevenir errores y deben respetarse, no suprimirse. Si desea definir una función con un nombre de Prelude, puede ocultarla usando

Importar ocultación de Preludio (mapa)

La sintaxis de ''ocultación'' solo se debe utilizar para Prelude y los módulos del mismo paquete, de lo contrario, corre el riesgo de que se rompa el código por cambios de API en el módulo importado.

Consulte: http://www.haskell.org/haskellwiki/Import_modules_properly


Ejemplo 1:

He vuelto a aprender a escribir analizadores en estilo aplicativo, son mucho más concisos. Por ejemplo, en lugar de:

funCallExpr :: Parser AST funCallExpr = do func <- atom token "(" arg <- expr token ")" return $ FunCall func arg

Yo en cambio escribo:

funCallExpr :: Parser AST funCallExpr = FunCall <$> atom <* token "(" <*> expr <* token ")"

Pero, ¿qué puedo decir? Si no te gusta la advertencia, desactívela como sugiere.

Ejemplo 2:

Sí, encuentro esa advertencia un poco irritante también. Pero me ha salvado un par de veces.

Se relaciona con las convenciones de nombres. Me gusta mantener los módulos bastante pequeños y mantener la mayoría de las importaciones calificadas (excepto para las importaciones de "notación" como Control.Applicative y Control.Arrow ). Eso mantiene bajas las posibilidades de conflicto de nombres, y hace que sea más fácil trabajar con ellas. hothasktags hace que este estilo sea tolerable si utiliza etiquetas.

Si solo está haciendo coincidir patrones en un campo con el mismo nombre, puede usar -XNamedFieldPuns o -XRecordWildCards para reutilizar el nombre:

data Foo = Foo { baz :: Int, bar :: String } -- RecordWildCards doubleBaz :: Foo -> Int doubleBaz (Foo {..}) = baz*baz -- NamedFieldPuns reverseBar :: Foo -> String reverseBar (Foo {bar}) = reverse bar

Otra convención común es agregar un prefijo húngaro a las etiquetas de registro:

data Foo = Foo { fooBaz :: Int, fooBar :: String }

Pero sí, no es divertido trabajar con los registros en Haskell. De todos modos, mantenga sus módulos pequeños y sus abstracciones ajustadas y esto no debería ser un problema. Considéralo como una advertencia que dice simplificar, hombre .