tipos sintaxis pattern multiplicar ejemplos datos contador ciclos basico basica haskell types typeclass

sintaxis - ¿Cuándo son necesarias las firmas de tipo en Haskell?



sintaxis basica de haskell (3)

Muchos textos introductorios le dirán que en Haskell las firmas de tipo son "casi siempre" opcionales. ¿Alguien puede cuantificar la parte "casi"?

Por lo que puedo decir, la única vez que necesita una firma explícita es desambiguar las clases de tipo. (El ejemplo canónico que se read . show .) ¿Hay otros casos en los que no haya pensado, o es esto?

(Soy consciente de que si vas más allá de Haskell 2010 hay muchas excepciones). Por ejemplo, GHC nunca inferirá los tipos N de rango. Pero los tipos N de rango son una extensión de idioma, no parte del estándar oficial [todavía]. )


Restricción de monomorfismo

Si tiene MonomorphismRestriction habilitada, a veces necesitará agregar una firma de tipo para obtener el tipo más general:

{-# LANGUAGE MonomorphismRestriction #-} -- myPrint :: Show a => a -> IO () myPrint = print main = do myPrint () myPrint "hello"

Esto fracasará porque myPrint es monomórfico. MonomorphismRestriction descomentar la firma de tipo para que funcione, o desactivar MonomorphismRestriction .

Restricciones fantasma

Cuando se pone un valor polimórfico con una restricción en una tupla, la tupla misma se vuelve polimórfica y tiene la misma restricción:

myValue :: Read a => a myValue = read "0" myTuple :: Read a => (a, String) myTuple = (myValue, "hello")

Sabemos que la restricción afecta la primera parte de la tupla pero no afecta la segunda parte. El sistema de tipo no lo sabe, lamentablemente, y se quejará si intenta hacer esto:

myString = snd myTuple

Aunque intuitivamente uno esperaría que myString fuera solo un String , el verificador de tipos necesita especializar la variable de tipo myString averiguar si la restricción está realmente satisfecha. Para hacer que esta expresión funcione, uno debería anotar el tipo de snd o myTuple :

myString = snd (myTuple :: ((), String))


En Haskell, como estoy seguro que sabes, los tipos se infieren . En otras palabras, el compilador determina qué tipo desea.

Sin embargo, en Haskell, también hay clases de tipos polimórficos, con funciones que actúan de diferentes maneras dependiendo del tipo de retorno. Aquí hay un ejemplo de la clase Monad, aunque no he definido todo:

class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b fail :: String -> m a

Nos dan muchas funciones solo con firmas de tipo. Nuestro trabajo es hacer declaraciones de instancias para diferentes tipos que pueden tratarse como mónadas, como Maybe t o [t] .

Eche un vistazo a este código; no funcionará de la manera que podríamos esperar:

return 7

Esa es una función de la clase Monad, pero debido a que hay más de una Monad, tenemos que especificar qué valor / tipo de retorno queremos, o se convierte automáticamente en IO Monad. Asi que:

return 7 :: Maybe Int -- Will return... Just 7 return 6 :: [Int] -- Will return... [6]

Esto se debe a que [t] y Maybe se han definido ambos en la clase de tipo Monad.

Aquí hay otro ejemplo, esta vez de la clase de tipos aleatorios. Este código arroja un error:

random (mkStdGen 100)

Debido a que el random arroja algo en la clase Random , tendremos que definir qué tipo queremos devolver, con un objeto de StdGen tupelo con el valor que queramos:

random (mkStdGen 100) :: (Int, StdGen) -- Returns... (-3650871090684229393,693699796 2103410263) random (mkStdGen 100) :: (Bool, StdGen) -- Returns... (True,4041414 40692)

Todo esto puede encontrarse en Aprender Haskell en línea, aunque tendrás que leer un poco. Esto, estoy casi 100% seguro, es el único momento en que los tipos son necesarios.


La recursión polimórfica necesita anotaciones tipo, en general.

f :: (a -> a) -> (a -> b) -> Int -> a -> b f f1 g n x = if n == (0 :: Int) then g x else f f1 (/z h -> g (h z)) (n-1) x f1

(Crédito: Patrick Cousot)

Observe cómo la llamada recursiva se ve mal escrita (!): Se llama a sí misma con cinco argumentos, ¡a pesar de que f tiene solo cuatro ! Luego recuerde que b puede crearse una instancia con c -> d , lo que hace que aparezca un argumento adicional.

El ejemplo inventado anterior computa

f f1 g n x = g (f1 (f1 (f1 ... (f1 x))))

donde f1 se aplica n veces. Por supuesto, hay una manera mucho más simple de escribir un programa equivalente.