سؤال

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?

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

As others have said, it's the unit type which has one value called unit. In Haskell syntax this is easily, if confusingly, expressed as () :: (). We can make our own quite easily as well.

data Unit = Unit

>>> :t Unit :: Unit
Unit :: Unit
>>> :t () :: ()
() :: ()

It's written as () because it behaves very much like an "empty tuple" would. There are theoretical reasons why this holds, but honestly it makes a lot of simple intuitive sense too.

It's often used as the argument to a type constructor like IO or ST when its the context of the value that's interesting, not the value itself. This is intuitively true because if I tell you have I have a value of type () then you don't need to know anything more---there's only one of them!

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 ()

It is both a type and a value.

It is unit type, the type that has only one value. In Haskell its name and only value looks like empty tuple : ().

As others have said, () in Haskell is both the name of the "unit" type, and the only value of said type.

One of the confusing things in moving from imperative programming to Haskell is that the way the languages deal with the concept of "nothing" is different. What's more confusing is the vocabulary, because imperative languages and Haskell use the term "void" to mean diametrically different things.

In an imperative language, a "function" (which may not be a true mathematical function) may have "void" as its return type, as in this pseudocode example:

void sayHello() {
    printLn("Hello!");
}

In this case, void means that the "function," if it returns, will not produce a result value. (The other possibility is that they function may not return—it may loop forever, or fail with an error or exception.)

In Haskell, however, all functions (and IO actions) must must produce a result. So when we write an IO action that doesn't produce any interesting return value, we make it return ():

sayHello :: IO ()
sayHello = putStrLn "Hello!"

Later actions will just ignore the () result value.

Now, you probably don't need to worry too much about this, but there is one place where this gets confusing, which is that in Haskell there is a type called Void, but it means something completely different from the imperative programming void. Because of this, the word "void" becomes a minefield when comparing Haskell and imperative languages, because the meaning is completely different when you switch paradigms.

In Haskell, Void is a type that doesn't have any values. The largest consequence of this is that in Haskell a function with return type Void can never return, it can only fail with an error or loop forever. Why? Because the function would have produce a value of type Void in order to return, but there isn't such a value.

This is however not relevant until you're working with some more advanced techniques, so you don't need to worry about it other than to beware of the word "void."

But the bigger lesson is that the imperative and the Haskell concepts of "no return value" are different. Haskell distinguishes between:

  1. Things that may return but whose result won't have any information (the () type);
  2. Things that cannot return, no matter what (the Void type).

The imperative void corresponds to the former, and not the latter.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top