bulk_send(
generate_expiry_email(user)
for user in db.getUsers()
if is_expired(user, date.now())
)
(...Just another flavour of syntax to look at) bulk_send(
generate_expiry_email(user)
for user in db.getUsers()
if is_expired(user, date.now())
)
(...Just another flavour of syntax to look at) Users.all()
|> Enum.filter(&Users.is_expired?(&1, Date.utc_today()))
|> Enum.map(&generate_expiry_email/1)
|> tap(&IO.inspect(label: "Expiry Email"))
|> Enum.reject(&is_nil/1)
|> bulk_send()
The nice thing here is that we can easily log to the console, and also filter out nil expiry emails. In production code, `generate_expiry_email/1` would likely return a Result (a tuple of `{:ok, email}` or `{:error, reason}`), so we could complicate this a bit further and collect the errors to send to a logger, or to update some flag in the db.It just becomes so easy to incrementally add functionality here.
---
Quick syntax reference for anyone reading:
- Pipelines apply the previous result as the first argument of the next function
- The `/1` after a function name indicates the arity, since Elixir supports multiple dispatch
- `&fun/1` expands to `fn arg -> fun(arg) end`
- `&fun(&1, "something")` expands to `fn arg -> fun(arg, "something") end`
Writing custom monad syntax is definitely quite a nice benefit of functional languages IMO.