haskell - uso - ¿Qué usan los paréntesis() en su propia media?
uso de parentesis y corchetes (4)
Leí en aprender que haskell eso
Los miembros de la enumeración son tipos ordenados secuencialmente ... Tipos en esta clase: () , Bool, Char ...
También aparece en algunas firmas:
putChar :: Char -> IO ()
Es muy difícil encontrar información al respecto en Google ya que las respuestas se refieren a los problemas de los "paréntesis comunes" (uso en llamadas a funciones, problemas de precedencia y similares).
Por lo tanto, ¿qué significa la expresión ()
? ¿Es un tipo? ¿Qué son las variables de tipo ()
? ¿Para qué se usa y cuándo se necesita?
Como han dicho otros, ()
en Haskell es tanto el nombre del tipo de "unidad" como el único valor de dicho tipo.
Una de las cosas confusas al pasar de la programación imperativa a Haskell es que la forma en que los idiomas tratan el concepto de "nada" es diferente. Lo que es más confuso es el vocabulario, porque los lenguajes imperativos y Haskell usan el término "vacío" para significar cosas diametralmente diferentes.
En un lenguaje imperativo, una "función" (que puede no ser una verdadera función matemática) puede tener "vacío" como su tipo de retorno, como en este ejemplo de pseudocódigo:
void sayHello() {
printLn("Hello!");
}
En este caso, void
significa que la "función", si se devuelve, no producirá un valor de resultado. (La otra posibilidad es que la función no pueda regresar; puede hacer un ciclo para siempre, o fallar con un error o excepción).
En Haskell, sin embargo, todas las funciones (y las acciones IO
) deben producir un resultado. Entonces, cuando escribimos una acción IO
que no produce ningún valor de retorno interesante, hacemos que devuelva ()
:
sayHello :: IO ()
sayHello = putStrLn "Hello!"
Las acciones posteriores simplemente ignorarán el valor del resultado ()
.
Ahora, probablemente no necesite preocuparse demasiado por esto, pero hay un lugar donde esto se vuelve confuso, que es que en Haskell hay un tipo llamado Void
, pero significa algo completamente diferente del imperativo void
programación. Debido a esto, la palabra "vacío" se convierte en un campo minado al comparar Haskell y los idiomas imperativos, porque el significado es completamente diferente cuando cambias de paradigma.
En Haskell, Void
es un tipo que no tiene ningún valor. La mayor consecuencia de esto es que en Haskell una función con el tipo de retorno Void
nunca puede regresar, solo puede fallar con un error o un bucle para siempre. ¿Por qué? Debido a que la función habría producido un valor de tipo Void
para regresar, pero no existe tal valor.
Sin embargo, esto no es relevante hasta que esté trabajando con algunas técnicas más avanzadas, por lo que no necesita preocuparse más que por tener cuidado con la palabra "vacío".
Pero la lección más importante es que el imperativo y los conceptos de Haskell de "ningún valor de retorno" son diferentes. Haskell distingue entre:
- Cosas que pueden regresar pero cuyo resultado no tendrá ninguna información (el tipo
()
); - Cosas que no pueden regresar, sin importar qué (el tipo
Void
).
El void
imperativo corresponde al primero, y no al segundo.
Como han dicho otros, es el tipo de unidad que tiene un valor llamado unidad. En la sintaxis de Haskell, esto se expresa fácilmente, aunque de manera confusa, como () :: ()
. Podemos hacer nuestro propio muy fácilmente también.
data Unit = Unit
>>> :t Unit :: Unit
Unit :: Unit
>>> :t () :: ()
() :: ()
Se escribe como ()
porque se comporta de manera muy parecida a una "tupla vacía". Hay razones teóricas por las que esto se sostiene, pero honestamente también tiene un gran sentido intuitivo.
A menudo se usa como argumento para un constructor de tipo como IO
o ST
cuando es el contexto del valor que es interesante, no el valor en sí. Esto es intuitivamente cierto porque si le digo que tengo un valor de tipo ()
entonces no necesita saber nada más, ¡solo hay uno de ellos!
putStrLn :: String -> IO () -- the return type is unimportant,
-- we just want the *effect*
map (const ()) :: [a] -> [()] -- this destroys all information about a list
-- keeping only the *length*
>>> [ (), (), () ] :: [()] -- this might as well just be the number `3`
-- there is nothing else interesting about it
forward :: [()] -> Int -- we have a pair of isomorphisms even
forward = length
backward :: Int -> [()]
backward n = replicate n ()
Es tanto un tipo como un valor. ()
es un tipo especial " unidad pronunciada", y tiene un valor: ()
, también unidad pronunciada. Es esencialmente el mismo que el tipo void
en Java o C / C ++. Si está familiarizado con Python, piense en él como el NoneType
que tiene el singleton None
.
Es útil cuando desea denotar una acción que no devuelve nada. Se utiliza más comúnmente en el contexto de las mónadas, como la mónada IO
. Por ejemplo, si tuvieras la siguiente función:
getVal :: IO Int
getVal = do
putStrLn "Enter an integer value:"
n <- getLine
return $ read n
Y por alguna razón, decidió que solo quería molestar al usuario y tirar el número que acaba de pasar:
getValAnnoy :: IO ()
getValAnnoy = do
_ <- getVal
return () -- Returns nothing
Sin embargo, el return
es solo una función de Monad
, por lo que podríamos abstraerlo un poco más.
throwAwayResult :: Monad m => m a -> m ()
throwAwayResult action = do
_ <- action
return ()
Entonces
getValAnnoy = throwAwayResult getVal
Sin embargo, no tiene que escribir esta función usted mismo, ya existe en Control.Monad
como la función void
que es aún menos restrictiva y funciona en Functor
s:
void :: Functor f => f a -> f ()
void fa = fmap (const ()) fa
¿Por qué funciona en Functor
lugar de Monad
? Bueno, para cada instancia de Monad
, puede escribir la instancia de Functor
como
instance Monad m => Functor m where
fmap f m = m >>= return . f
Pero no puedes hacer una Monad
con cada Functor
. Es como un cuadrado es un rectángulo, pero un rectángulo no siempre es un cuadrado, las mónadas forman un subconjunto de Functors.
Es tanto un tipo como un valor.
Es tipo de unidad , el tipo que tiene un solo valor. En Haskell, su nombre y único valor se parecen a una tupla vacía: ()
.