8 ms·
In Elixir this would be written as: db.getUsers() |> getExpiredUsers(Date.now()) |> generateExpiryEmails() |> email.bulkSend() I think Elixir hits the
by POiNTx 11mo ago
In Elixir this would be written as:
db.getUsers()
|> getExpiredUsers(Date.now())
|> generateExpiryEmails()
|> email.bulkSend()
I think Elixir hits the nail on the head when it comes to finding the right balance between functional and imperative style code.
- montebicyclelo 11mo agobulk_send( generate_expiry_email(user) for user in db.getUsers() if is_expired(user, date.now()) ) (...Just another flavour of syntax to look at)
- Akronymus 11mo agoNot sure I like how the binding works for user in this example, but tbh, I don't really have any better idea. Writing custom monad syntax is definitely quite a nice benefit of functional languages IMO.
- whichdan 11mo agoThe nice thing with the Elixir example is that you can easily `tap()` to inspect how the data looks at any point in the pipeline. You can also easily insert steps into the pipeline, or reuse pipeline steps. And due to the way modules are usually organized, it would more realistically read like this, if we were in a BulkEmails module: 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`
- time4tea 11mo agoNot a single person in this thread commented on the use of Date.now() and similar - surely clock.now() - you never ever want to use global time in any code, how could you test it? clock in this case is a thing that was supplied to the class or function. It could just be a function: () -> Instant. (Setting a global mock clock is too evil, so don't suggest that!)
- POiNTx 11mo agoI was just referring to how pipes make these kinds of chained function calls more readable. But on your point, I think using Date.now() is perfectly ok.
- vlovich123 11mo agoWhat happens during a daylight savings adjustment?
- POiNTx 11mo agoYou use UTC which doesn't adjust daylight savings.
- ruszki 11mo ago> I think using Date.now() is perfectly ok. This is why we have tests which we need to update every 3 months, because somebody said this. This is of course, after a ton of research went into finding out why the heck our tests broke suddenly.