Chapter 6: Patterns all the way down
Newsletter
- ?
- Hex packages
- files
This one uses simplifile,
a Gleam package from Hex. bea add simplifile puts it in
bea.toml, and from then on it is an ordinary import.
emails = read_emails(emails_path)? is the ?
operator at its most useful: each step either gives you its value or
stops the function with its error. The Gleam tab shows the
use … <- result.try(…) chain you didn't have to write.
This module is a library with no main, so there is no
output to show.
import simplifile
pub def read_emails(path: String) -> Result(List(String), Nil) {
data_str = simplifile.read(path) |> result.replace_error(Nil)?
data_str
|> string.trim
|> string.split("\n")
|> fn(x) {
Ok(x)
}
}
pub def create_log_file(path: String) -> Result(Nil, Nil) {
simplifile.write(path, "") |> result.replace_error(Nil)
}
pub def log_sent_email(path: String, email: String) -> Result(Nil, Nil) {
simplifile.append(path, "#{email}\n") |> result.replace_error(Nil)
}
def try_send(email, send_fn, log_path) {
if send_fn(email) == Ok(Nil) {
log_sent_email(log_path, email)
} else {
Error(Nil)
}
}
pub def send_newsletter(
emails_path: String,
log_path: String,
send_email: fn(String) -> Result(Nil, Nil),
) -> Result(Nil, Nil) {
emails = read_emails(emails_path)?
create_log_file(log_path)?
_ = list.each(emails, fn(email) {try_send(email, send_email, log_path)})
Ok(Nil)
}import gleam/list
import gleam/result
import gleam/string
import simplifile
pub fn read_emails(path: String) -> Result(List(String), Nil) {
use data_str <- result.try(simplifile.read(path) |> result.replace_error(Nil))
data_str |> string.trim |> string.split("\n") |> fn(x) { Ok(x) }
}
pub fn create_log_file(path: String) -> Result(Nil, Nil) {
simplifile.write(path, "") |> result.replace_error(Nil)
}
pub fn log_sent_email(path: String, email: String) -> Result(Nil, Nil) {
simplifile.append(path, email <> "\n") |> result.replace_error(Nil)
}
fn try_send(email, send_fn, log_path) {
case send_fn(email) == Ok(Nil) {
True -> log_sent_email(log_path, email)
False -> Error(Nil)
}
}
pub fn send_newsletter(
emails_path: String,
log_path: String,
send_email: fn(String) -> Result(Nil, Nil),
) -> Result(Nil, Nil) {
use emails <- result.try(read_emails(emails_path))
use _ <- result.try(create_log_file(log_path))
let _ = list.each(emails, fn(email) { try_send(email, send_email, log_path) })
Ok(Nil)
}An exercism exercise from the Gleam track, solved in Bea: beacode/src/e24_newsletter.bea