You can ask ghci
for precedence information:
Prelude Control.Applicative> :i <|>
class Applicative f => Alternative f where
...
(<|>) :: f a -> f a -> f a
...
-- Defined in `Control.Applicative'
infixl 3 <|>
Prelude Control.Applicative> :i <*
class Functor f => Applicative f where
...
(<*) :: f a -> f b -> f a
-- Defined in `Control.Applicative'
infixl 4 <*
The relevant bits there are these two lines:
infixl 3 <|>
infixl 4 <*
Since <*
has a higher precedence (4), it binds tighter; so yes, the brackets are needed to prevent that from being parsed as expr1 <|> (expr2 <* expr3)
.