I'm the author. This library is still very early in development (two days of dev time). Its great that its on HN front page but I just want to manage expectations if you try to use the library. It is still very alpha.
While I like the composability from a readability standpoint, is this creating unnecessary VM GC churn due to needing to maintain immutability of the %Mail{} struct? In other words, would it be better if you could just apply all attributes at once (with one function call, say "put", that took, say, a Map)? Or does that basically destroy this convention. lol
I doubt this is a performance issue, because it's the same pattern Phoenix uses to respond to an html request. Phoenix is very fast despite passing the much larger Plug.Conn struct through a much larger number of functions that similarly update one thing and return a new copy.
I'd also be curious to know what happens with GC behind the scenes for this pattern to be so performant - I don't know anything about that part.
It will be a performance issue at some point. It creates a lot of allocator & GC churn.
Part of the reason this is mitigated somewhat has to do with how the Beam VM manages GC. It's per process, which provides a few nice features. One is that processes that do a lot of "work" tend not to be long-lived, and consequently they tend not to accrete a lot of heap allocations. The share-nothing concurrency model enforces an isolation pattern on the developer, which then allows the GC to make some guaranteed assumptions about what is safe and isn't safe to reap. The combination of those to factors allows GC sweeps to be both typically small/short and also done in parallel with other scheduled running processes, so that GC doesn't block all the execution resources.
Considering the use case here though, I'd really not worry about it. The only way it'd likely bite you is if you were building a massive scale email pushing application, and you were weirdly trying to optimize for using as low-end, or as few, machines as possible to run it, and you hadn't already found yourself flagged by every mail relay as being a spammer.
> Phoenix is very fast despite passing the much larger Plug.Conn struct through a much larger number of functions that similarly update one thing and return a new copy.
I know that, but I'm not concerned about the current, apparent speed. I'm concerned about the "death by a million cuts" I've seen in other large apps I've worked on, where incremental tiny but unnecessary delays eventually add up to something significant. In my experience the only way to slow down that process is to be really anal about performance, memory consumption etc. with every line of code you write.
It's probably worth at some point adding at least one example of actually then sending them mail somewhere via a local sendmail binary, and one for via SMTP.
IME if you provide an email sending library without email generation suggestions, or vice versa, yours users will do something completely unexpected (and probably not to your taste) for the other half.
(note that I'm explicitly not complaining that you didn't already do that, given how recently you got started - just trying to make sure it's on your list of stuff to do because bitter experience says it's more important than you might initially expect :)
How is this any different then https://github.com/antp/mailer? On mailer you can do the same compose using add_to, add_subject, etc... even though they don't show it on the readme.
This is old, I willing to bet Elixir and Pheonix are better then Go now. It isn't all about performance though. I find Elixir easier to read and maintain over Go as well.
Since Elixir doesn't have mandatory types, I assume there's no way to verify at compile time that it's a "valid" email (Has a recipient, and a from addr, whatever other requirements), and you'd have to check that in tests or at runtime?
The parent wasn't talking about the individual validation of email addresses (although I guess that may be a part of the answer), but instead about checking the presence of a recipient, subject, sender etc as part of static typing.
I've got to strongly disagree with you on this. This library is a day old and there are already descriptions and examples on every public function. This is an example of the Elixir community's EXCELLENT documentation.
I don't (currently) have a need for this so I'm not going to play with it right now, but I like that it looks idiomatically like Plug. Pass a struct through a pipeline until it's ready to go.