2012-05-15 8 views
20

¿Ayuda al compilador a optimizar, o es solo un trabajo excedente agregar firmas de tipos adicionales? Por ejemplo, a menudo se ve:¿Por qué es tan raro usar firmas de tipo en cláusulas where?

foo :: a -> b 
foo x = bar x 
     where bar x = undefined 

En lugar de:

foo :: a -> b 
foo x = bar x 
     where bar :: a -> b 
      bar x = undefined 

Si Omito la firma de tipo de nivel superior, GHC me da una advertencia, por lo que si no consigo advertencias estoy bastante Confío en que mi programa es correcto. Pero no se emiten advertencias si omito la firma en una cláusula where.

+0

No puedo responder la pregunta de si hará que la compilación sea más rápida, pero siempre es una buena práctica escribir firmas de tipo si se trata de una función no trivial. – Wes

+7

Además, muchas de estas definiciones locales necesitan acceder a las variables de tipo del ámbito externo, lo que tiende a saturar el código con 'asTypeOf' y amigos, a menos que use' ScopedTypeVariables'. – Vitus

+1

Si su función es lo suficientemente importante como para obtener una declaración de tipo, ¿por qué no convertirla en un ciudadano de primera clase? – rotskoff

Respuesta

17

A menudo las definiciones en las cláusulas where son para evitar repetirse si una sub-expresión aparece más de una vez en una definición. En tal caso, el programador piensa en la definición local como un simple sustituto para escribir las subexpresiones en línea. Por lo general, no escribiría explícitamente las subexpresiones en línea, por lo que tampoco debe escribir la definición where. Si lo está haciendo para ahorrar en tipeo, entonces la declaración tipo mataría todos sus ahorros.

Parece bastante común introducir where a los alumnos de Haskell con ejemplos de esa forma, por lo que siguen pensando que "estilo normal" es no dar declaraciones de tipo para las definiciones locales. Al menos, esa fue mi experiencia aprendiendo Haskell. Desde entonces, descubrí que muchas de mis funciones que son lo suficientemente complicadas como para necesitar un bloque where se vuelven bastante inescrutables si no conozco el tipo de definiciones locales, así que trato de equivocarme para escribirlas siempre; incluso si creo que el tipo es obvio mientras escribo el código, puede que no sea tan obvio cuando lo estoy leyendo después de no haberlo mirado por un tiempo. Un poco de esfuerzo para mis dedos casi siempre se ve compensado por incluso uno o dos casos de tener que ejecutar inferencias tipo en mi cabeza.

respuesta de Ingo da una buena razón para deliberadamente no dar un tipo a una definición local, pero sospecho que la razón principal es que muchos programadores han asimilado la regla de oro prever que las declaraciones de tipo para los mejores definiciones de los niveles, pero no para las definiciones locales de la forma en que aprendieron Haskell.

0

Agregar una firma de tipo puede hacer que su código sea más rápido. Tomemos como ejemplo el siguiente programa (Fibonacci):

result = fib 25 ; 
-- fib :: Int -> Int 
fib x = if x<2 then 1 else (fib (x-1)) + (fib (x-2)) 
  • Sin la anotación en la segunda línea, se necesita 0,010 seg. correr.
  • Con la anotación Int -> Int, tarda 0.002 segundos.

Esto sucede porque si usted no dice nada acerca de fib, que va a ser escrito como fib :: (Num a, Num a1, Ord a) => a -> a1, lo que significa que durante el tiempo de ejecución, estructuras de datos adicionales ("diccionarios") tendrán que pasar entre las funciones de representan las clases de tipo Num/Ord.

+1

La pregunta se refiere a las firmas de tipos de definiciones locales. En realidad, es muy común dar una firma de tipo a las definiciones de nivel superior. – Vitus

+2

Estoy de acuerdo con Vitus, esta respuesta es menos relevante para las definiciones locales. Es muy común escribir definiciones de alto nivel que, sin una firma de tipo, se le asignan tipos más generales de los que podría haber necesitado y, por lo tanto, se ejecutan más despacio. Las definiciones locales casi siempre están limitadas por la firma de tipo de la definición de nivel superior en la que se producen, por lo que este problema no surge con tanta frecuencia si está escribiendo sus definiciones de nivel superior. Incluso si la definición local no está formalmente restringida, el compilador sabe que es local y no necesita ser más general que su uso local. – Ben

11

A menudo, las declaraciones where se utilizan para elementos locales cortos, que tienen tipos simples o tipos que se infieren fácilmente. Como resultado, no hay beneficio para el humano o el compilador para agregar el tipo.

Si el tipo es complejo o no se puede inferir, es posible que desee agregar el tipo.

Si bien las firmas de tipo monomórficas pueden hacer que las funciones de nivel superior sean más rápidas, no es tan beneficioso para las definiciones locales en las cláusulas where, ya que GHC alineará y optimizará las definiciones en la mayoría de los casos.

20

Existe una clase de funciones locales cuyos tipos no se pueden escribir en Haskell (sin usar extensiones GHC sofisticadas). Por ejemplo:

f :: a -> (a, Int) 
f h = g 1 
    where g n = (h, n) 

Esto es porque mientras el a en el tipo de firma f es visto desde fuera polimórfica f, esto no es así desde dentro f. En g, es algún tipo desconocido, pero no cualquier tipo, y (estándar) Haskell no puede expresar "el mismo tipo que el primer argumento de la función en la que está definido" en su lenguaje de tipos.

+4

Aunque '{- # LANGUAGE ScopedTypeVariables # -}' le permite asignar el tipo 'g :: Int -> (a, Int)' si modifica la firma de tipo para 'f' para que sea' forall a. a -> (a, Int) '. –

Cuestiones relacionadas