What is an expression in Haskell?

Yes, 1 + 1 is an expression. Yes, 8 is an expression. Whether 8 reduces further is a slightly complicated question, just because of a strange detail of Haskell: numbers are polymorphic. At the usual types like Int and Double, 8 does not reduce meaningfully; but one could add a user-made instance where 8 could reduce further.

...but those are sort of annoying language-lawyer-y quibbles. In the big picture, everything you said is essentially right.


I read that there are no statements, just expressions in Haskell.

To elaborate on @amalloy's comments about there being more than expressions in Haskell: You normally make the distinction between statements and expressions in imperative languages because you have something like x = 2 + 2; with the x = ...; part being a statement and the 2 + 2 part being an expression.

The body of a Haskell function is always one single expression (although with where you can split that one expression apart for convenience), and this is the main distinction that drives the question. So if you want to "do more than one thing", which is an imperative notion of a function being able to change global state, you solve this with monads, like so:

{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Web.Scotty

main :: IO ()
main = scotty 3000 $
  get "/:who" $ do
    who <- param "who"
    text ("Beam " <> who <> " up, Scotty!")

Here, main's body (a monadic action, not a function) is a single expression, scotty 3000 (...). While the linebreak after scotty 3000 $ doesn't carry meaning and only makes the code look nicer, the linebreak in the do block actually reduces multiple actions into one expression via syntactic sugar. So while it may seem that this event handler does two things things: (1) param "who", (2) text (...), it is still one expression equivalent to this:

main =
  scotty 3000 (get "/:who" (param "who" >>= (\who -> text ("Beam " <> who <> " up, Scotty!"))))

with >>= being the invisible operator between the do-block lines. When expressions begin to grow, this becomes very inconvenient, so you split parts of them into sub-expressions and give those names, e.g. like:

main = scotty 3000 handler
  where
    handler = do
      get "/:who" getWho
      post "/" postWho

    getWho = do
      ...

    postWho = do
      ...

But it is essentially equivalent to one big expression.

This is the point where some people think they should make their own web-framework. :-D

There are many things in the language beyond function bodies that are not expressions; in the example above, the following are not expressions:

  • {-# LANGUAGE OverloadedStrings #-} (a language pragma)
  • module Main (main) where (a module, export list)
  • import Web.Scotty (an import declaration)
  • main :: IO () (a type signature)
  • main = (a top declaration, or a value binding)

Of these, I think import Web.Scotty could be called a kind of statement, since grammatically it's in imperative form, but if we're going to be imprecise, I'd prefer to call them all declarations.

More interestingly, in Haskell you have both an expression language at the value level and one at the type level. So IO () isn't a value expression, but it's a type expression. If you had the ability to mix those two expression languages up, you'd have dependent types.

Tags:

Haskell