Buscar..


Observaciones

Una de las fortalezas de Haskell es la capacidad de aprovechar el sistema de tipos para modelar partes de su dominio de problemas en el sistema de tipos. Al hacerlo, a menudo se encuentran tipos muy complejos. Cuando se escriben programas con estos tipos (es decir, con valores que tienen estos tipos) ocasionalmente se vuelve casi inmutable para "hacer malabares" con todos los tipos. A partir de GHC 7.8, hay una nueva característica sintáctica llamada agujeros mecanografiados. Los orificios escritos no cambian la semántica del lenguaje central; están destinados puramente como una ayuda para la escritura de programas.

Para obtener una explicación detallada de los agujeros escritos, así como una discusión sobre el diseño de los agujeros escritos, consulte la wiki de Haskell .


Sección de la guía del usuario de GHC sobre agujeros mecanografiados .

Sintaxis de los agujeros mecanografiados.

Un agujero escrito es un guión bajo ( _ ) o un identificador de Haskell válido que no está en el alcance, en un contexto de expresión. Antes de la existencia de orificios escritos, ambas cosas podrían provocar un error, por lo que la nueva sintaxis no interfiere con ninguna sintaxis antigua.

Controlando el comportamiento de los agujeros mecanografiados.

El comportamiento predeterminado de los agujeros escritos es producir un error en tiempo de compilación cuando se encuentra un agujero escrito. Sin embargo, hay varias banderas para afinar su comportamiento. Estas banderas se resumen de la siguiente manera ( trac de GHC ):

Por defecto, GHC ha habilitado los orificios y ha producido un error de compilación cuando encuentra un orificio escrito.

Cuando -fdefer-type-errors o -fdefer-typed-holes está habilitado, los errores de los hoyos se convierten en advertencias y resultan en errores de tiempo de ejecución cuando se evalúan.

El indicador de advertencia -fwarn-typed-holes está -fwarn-typed-holes forma predeterminada. Sin los -fdefer-type-errors -fdefer-typed-holes o los -fdefer-typed-holes esta bandera no es -fdefer-typed-holes , ya que los agujeros escritos son un error en estas condiciones. Si cualquiera de las banderas de aplazamiento está habilitada (convirtiendo los errores de orificio escritos en advertencias), la bandera de -fno-warn-typed-holes desactiva las advertencias. Esto significa que la compilación se realiza de forma silenciosa y la evaluación de un agujero producirá un error de tiempo de ejecución.

Semántica de agujeros mecanografiados.

Se puede decir simplemente que el valor de un agujero de tipo undefined está undefined , aunque un agujero escrito genera un error en tiempo de compilación, por lo que no es estrictamente necesario asignarle un valor. Sin embargo, un agujero escrito (cuando están habilitados) produce un error de tiempo de compilación (o advertencia con errores de tipo diferido) que indica el nombre del agujero escrito, su tipo más general inferido y los tipos de enlaces locales. Por ejemplo:

Prelude> \x -> _var + length (drop 1 x)

<interactive>:19:7: Warning:
    Found hole `_var' with type: Int
    Relevant bindings include
      x :: [a] (bound at <interactive>:19:2)
      it :: [a] -> Int (bound at <interactive>:19:1)
    In the first argument of `(+)', namely `_var'
    In the expression: _var + length (drop 1 x)
    In the expression: \ x -> _var + length (drop 1 x)

Tenga en cuenta que en el caso de los orificios escritos en las expresiones ingresadas en la respuesta de GHCi (como anteriormente), también se informó el tipo de expresión ingresada, ya it (aquí de tipo [a] -> Int ).

Usando agujeros escritos para definir una instancia de clase

Los orificios escritos pueden facilitar la definición de funciones, a través de un proceso interactivo.

Supongamos que desea definir una instancia de clase Foo Bar (para su tipo de Bar personalizado, para usarla con alguna función de biblioteca polimórfica que requiere una instancia de Foo ). Ahora, tradicionalmente, buscaría la documentación de Foo , descubriría qué métodos necesita definir, escrutar sus tipos, etc. - pero con los agujeros tipificados, puede omitir eso.

Primero solo define una instancia ficticia:

instance Foo Bar where

El compilador ahora se quejará

Bar.hs:13:10: Warning:
No explicit implementation for
  ‘foom’ and ‘quun’
In the instance declaration for ‘Foo Bar’

Ok, entonces necesitamos definir foom para Bar . ¿Pero qué se supone que es eso? Una vez más, somos demasiado perezosos para mirar en la documentación, y solo pregunte al compilador:

instance Foo Bar where
  foom = _

Aquí hemos utilizado un agujero escrito como una simple "consulta de documentación". Las salidas del compilador

Bar.hs:14:10:
    Found hole ‘_’ with type: Bar -> Gronk Bar
    Relevant bindings include
      foom :: Bar -> Gronk Bar (bound at Foo.hs:4:28)
    In the expression: _
    In an equation for ‘foom’: foom = _
    In the instance declaration for ‘Foo Bar’

Observe cómo el compilador ya ha llenado la variable de tipo de clase con la Bar tipo concreto para la que queremos crear una instancia. Esto puede hacer que la firma sea mucho más fácil de entender que la polimórfica que se encuentra en la documentación de la clase, especialmente si se trata de un método más complicado, por ejemplo, una clase de tipo de múltiples parámetros.

¿Pero qué diablos es Gronk ? En este punto, probablemente sea una buena idea preguntarle a Hayoo . Sin embargo, aún podemos evitarlo: como una suposición ciega, asumimos que esto no es solo un constructor de tipo sino también el constructor de un solo valor, es decir, se puede usar como una función que de alguna manera producirá Gronk a valor Gronk a . Así que intentamos

instance Foo Bar where
  foom bar = _ Gronk

Si tenemos suerte, Gronk es en realidad un valor, y el compilador ahora dirá

    Found hole ‘_’
      with type: (Int -> [(Int, b0)] -> Gronk b0) -> Gronk Bar
    Where: ‘b0’ is an ambiguous type variable

Ok, eso es feo. Al principio solo note que Gronk tiene dos argumentos, así que podemos refinar nuestro intento:

instance Foo Bar where
  foom bar = Gronk _ _

Y esto ahora es bastante claro:

    Found hole ‘_’ with type: [(Int, Bar)]
    Relevant bindings include
      bar :: Bar (bound at Bar.hs:14:29)
      foom :: Bar -> Gronk Bar (bound at Foo.hs:15:24)
    In the second argument of ‘Gronk’, namely ‘_’
    In the expression: Gronk _ _
    In an equation for ‘foom’: foom bar = Gronk _ _

Ahora puede seguir avanzando, por ejemplo, deconstruyendo el valor de la bar (los componentes se mostrarán, con tipos, en la sección Relevant bindings ). A menudo, en algún punto es completamente obvio cuál será la definición correcta, porque ves todos los argumentos disponibles y los tipos encajan como un rompecabezas. O alternativamente, puedes ver que la definición es imposible y por qué.

Todo esto funciona mejor en un editor con compilación interactiva, por ejemplo, Emacs con modo haskell. A continuación, puede usar orificios escritos de forma muy parecida a las consultas de valor de mouse-over en un IDE para un lenguaje imperativo dinámico interpretado, pero sin todas las limitaciones.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow