| coming from erlang elixir is much nicer than that. exunit is miles ahead of eunit and i feel like that would be enough to convince me to switch. the with syntax is nice and helps to deal with early exit on errors but still feels a bit awkward compared to imperative control flow. like you can do something like this in an imperative language: foo, err = func()
if err != nil {
return nil, Err("bad")
}
bah, err = func2(foo)
if err != nil {
return nil, Err("blah")
}
return bah
whereas in elixir you have to do something like the following in order to avoid the nesting of doom: with {ok, foo} <- func() |> Error.or({:error, "bad"}),
{ok, bah} <- func2(foo) |> Error.or({:error, "blah"}), do
bah
end
like the with blocks kind of work until you have to transform the errors you receive then you need helper functions. if you are just doing static transforms like above it is not too bad but it starts to get hairy if you want to transform based on the returned error or variables in the function. whereas the imperative style you can inline your error handling logic nicely into the function.for example what if i want to log something on the error path that includes some contextual information and custom formatting. probably, the easiest way is going to be to use with() and pipe to a custom function that triggers on the error path to do the logging because the code is going to start getting really messy. whereas if i was writing it imperatively i could just inline the logging statement most of the time because it is just a few lines of code. foo, err = func()
if err != nil {
return nil, Err("bad")
}
bah, err = func2(foo)
if err != nil {
Logger.error("err: " + err + " when processing: " + foo.name)
return nil, Err("blah")
}
return bah
like i feel this is a bit messy but maybe it is actually not that bad: with {ok, foo} <- func() |> Error.or({:error, "bad"}),
{ok, bah} <- func2(foo) |> Error.or_else(fn err ->
Logger.error("err: " <> err <> " when processing: " <> foo.name)
{:error, "blah"}
end), do
bah
end
|
I've been using Elixir and Phoenix for a customer for a couple of years. It's ok to great, especially when spawning jobs, with some stains.
I'm not a great fan of the with syntax. I wish they implemented it as a native statement of the language instead of as a macro. In that way they probably could let us write the same code inside and outside a with, instead of having to transform = into <- and add a comma.
But the worst offenders are GenServers. They should really have the syntax of OO classes instead of the incomprehensible handle* functions. After all that's what they are, objects with their own CPU. (Remember Armstrong about Erlang being the only true OO language?)
By the way, that would make it easier to code, to understand and to migrate people from imperative languages.