I read that there are no statements, just expressions in Haskell. I think an expression is something that evaluates (reduces) to a value. 1 + 1
is an expression, right? It reduces to 2
, but how about a number alone: 8
?
Is that 8
alone also considered to be an expression? An expression that doesn't reduce any further? I would be grateful for any clarification.
In fact, Haskell takes this principle to the extreme: everything in Haskell is an expression, and even statements are expressions. For example, the following code might appear to be a traditional imperative-style sequence of statements: main = do putStrLn "Enter a number:" -- Statement?
The case expression in Haskell Many imperative languages have Switch case syntax: we take a variable and execute blocks of code for specific values of that variable. We might also include a catch-all block of code in case the variable has some value for which we didn't set up a case.
An expression is evaluated by normal order (leftmost outermost redex first).
A guard is basically a boolean expression. If it evaluates to True, then the corresponding function body is used. If it evaluates to False, checking drops through to the next guard and so on. If we call this function with 24.3, it will first check if that's smaller than or equal to 18.5.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With