Search code examples
haskellparentheses

What do parentheses () used on their own mean?


I read in learn you haskell that

Enum members are sequentially ordered types ... Types in this class: (), Bool, Char ...

Also it appears in some signatures:

putChar  ::    Char -> IO ()

It is very difficult to find info about it in Google as the answers refer to problems of the "common parentheses" (use in function calls, precedence problems and the like).

Therefore, what does the expression () means? Is it a type? What are variables of type ()? What is used for and when is it needed?


Solution

  • It is both a type and a value. () is a special type "pronounced" unit, and it has one value: (), also pronounced unit. It is essentially the same as the type void in Java or C/C++. If you're familiar with Python, think of it as the NoneType which has the singleton None.

    It is useful when you want to denote an action that doesn't return anything. It is most commonly used in the context of Monads, such as the IO monad. For example, if you had the following function:

    getVal :: IO Int
    getVal = do
        putStrLn "Enter an integer value:"
        n <- getLine
        return $ read n
    

    And for some reason you decided that you just wanted to annoy the user and throw away the number they just passed in:

    getValAnnoy :: IO ()
    getValAnnoy = do
        _ <- getVal
        return ()  -- Returns nothing
    

    However, return is just a Monad function, so we could abstract this a bit further

    throwAwayResult :: Monad m => m a -> m ()
    throwAwayResult action = do
        _ <- action
        return ()
    

    Then

    getValAnnoy = throwAwayResult getVal
    

    However, you don't have to write this function yourself, it already exists in Control.Monad as the function void that is even less constraining and works on Functors:

    void :: Functor f => f a -> f ()
    void fa = fmap (const ()) fa
    

    Why does it work on Functor instead of Monad? Well, for each Monad instance, you can write the Functor instance as

    instance Monad m => Functor m where
        fmap f m = m >>= return . f
    

    But you can't make a Monad out of every Functor. It's like how a square is a rectangle but a rectangle isn't always a square, Monads form a subset of Functors.