I agree that the stability is quite nice. But there are plenty of areas where the language could use some improvement - for instance, the ergonomics around writing nested Single Page Apps is pretty cumbersome.
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.