Infix operators are symbols that alias binary (2-argument) [[Functions|functions]]. They're defined like so: ```haskell infixl as -- or infixr -- .. ``` e.g. ```haskell eq :: Int -> Int -> Boolean eq = -- ... add :: Int -> Int -> Int add = -- ... infixl 1 add as + infixl 1 eq as == (1 + 2) == 3 -- same as eq (add 1 2) 3 ``` ## Associativity Operators are either left or right associative (`infixl` or `infixr`). When multiple infix operators with the same precedence are chained, associativity tells the language how to group them, e.g. `&&` is right-associative, while `*` is left associative. e.g. ```haskell a && b && c -- interpreted as (a && (b && c)) a * b * c -- interpreted as ((a * b) * c) ``` ## Precedence The precedence of operators is an int from 1-9 used as a tie-break, for example in ```haskell a + b == c ``` this behaves how you'd expect; this is equivalent to ```haskell ((a + b) == c) ``` as the precedence of `+` (6) is higher than `==`'s (4), the grouping is first done around `a + b`. ## Common Operators |Operator|Associativity|Precedence|Aliases| |--|--|--|--| |`$`|||| ## Directionality Most commonly used operators have flipped variants, e.g. - function composition has `g <<< f` or `f >>> g` - function application has `f $ a` or `a # f` - [[Functor|map]] has `f <$> a` or `a <#> f` - [[Bind|bind]] has `f =<< m` or `m >>= f` In general, right-to-left operators tend to be easier to refactor into & out of because they closely mirror the expressions they replace: ```haskell map (add 1) maybeNum -- into add 1 <$> maybeNum foo (bar a) -- into foo <<< bar ``` Left-to-right, on the other hand, can read better to humans and plays better with pipelines containing both [[Bind|bind `>>=`]] and [[Functor|map `<#>`]]. Consider an expression piping `Maybe String` to `split :: String -> Array String` then `Data.Array.NonEmpty.fromArray :: Array a -> Maybe (NonEmptyArray a)`, then `toLower` each element: ```haskell String.toLower <$> ( Array.NonEmpty.fromArray =<< String.split "." <$> mStr ) ``` We need to wrap the right hand of the last `map` because the precedence of `=<<` is `1` (the lowest) and the precedence of `<$>` is `4`. Written RTL, though, gives: ```haskell mStr <#> String.split "." >>= Array.NonEmpty.fromArray <#> String.toLower ``` This works because `<#>`'s precedence (1) is the same as `>>=`. The lower precedence on flipped map means you'll often need more parentheses wrapping its arguments `(..) <#> (..) >>= (..)` as opposed to entire expressions `.. <$> (.. =<< ..)`. Personally, I try to stick to RTL except for expressions including bind.