Pipelines & let
Two small tools do a lot for readability: the pipe operator and let … in.
The pipe |>
x |> f is just f x. On its own that's not much, but chained it turns
inside-out nesting into a top-to-bottom recipe:
-- without pipes — read it right-to-left, inside-out
result1 =
String.toUpper (String.trim (String.toLower name))
-- with pipes — read it top-to-bottom
result2 =
name
|> String.toLower
|> String.trim
|> String.toUpper
Both compute the same thing. The piped version reads like the steps happen in order, because they do: the value on the left flows into each function in turn.
There's also <| (apply to the right), handy for dropping a pair of parentheses:
println <| String.fromInt (1 + 2)
-- same as: println (String.fromInt (1 + 2))
let … in for naming steps
When a computation has intermediate values worth naming, let … in makes it
legible:
priceWithTax : Float -> Float
priceWithTax base =
let
taxRate = 0.2
tax = base * taxRate
in
base + tax
Bindings in a let can refer to each other regardless of order — Sky sorts out
the dependencies — so you can write them in whatever order reads best:
let
total = subtotal + shipping
subtotal = 100
shipping = 5
in
total -- 105
Pipelines and let compose: a common shape is a let that names a couple of
inputs, then a piped expression that transforms them. You'll see this everywhere
in real Sky code.