- it might be easier to start your app with `application:ensure_all_started(erl2048)`. If your dependencies are well set in your app file, this will boot all dependencies for you.
- Given you've already compiled, you don't need the `rebar compile skip_deps=true` part to start the app -- that's just to recompile before you boot it, but I guess you knew that already.
- Eventually you get to look in releases, but they're a bit more complex. Look at http://relx.org for an easier way in. Releases will let your system know how to boot apps (so you don't have to `-eval` anything), and lets you pre-configure shell options (`-noshell -detached`) so that you can just boot the server with a `./_rel/bin/my-release` call.
- The naming convention for Erlang functions is `some_function`, not `someFunction`. This makes it easier to visually parse between variables (`SomeVal`) and functions (`some_function`).
- functions like `withinBounds({X, Y})` can be rewritten as `within_bounds({X,Y}) -> (X > 0), (X =< ?SIZE) andalso (Y > 0), (Y =< ?SIZE).` -- no need to pattern match there past the initial tuple. Remove the `_` matching, and just let it crash if something doesn't fit. You'll see it in logs (somewhere) and can fix it later.
- Functions like `getVector(up) -> {-1,0}` could easily be made declarative by using `vector(up) -> {-1,0}` or just `up() -> {-1,0}`.
- Break out large functions more. There's often no reason to have a 100+ lines function in Erlang. If you end up nesting `case` constructs more than two level deeps, start thinking about splitting the functions up and deferring stuff to other function calls. Your code will also be more testable that way, easier to reason about when something goes wrong, and easier to delete and refactor without breaking anything.
Another tool you might want to check out is erlang.mk, by the author of the Cowboy webserver, Loïc Hoguin -- from the announcement[1]:
erlang.mk is a rebar replacement. It was initially created for allowing a faster development process than rebar and for better compatibility with Linux build tools. It should work on Linux and OSX with GNU Make installed.
Here's how erlang.mk and relx can be used together to build releases (this is from a post by Loïc)[2]:
There is two steps to building a release. First you need to build the various OTP applications you want to include in the release. Once done, you need to create the release itself, by including the Erlang runtime system alongside the applications, a boot script to start the node and all its applications, and some configuration files.
erlang.mk solves the first step. It is an include file for GNU Make. Just including it in a Makefile is enough to allow building your project, fetching and building dependencies, building documentation, performing static analysis and more.
relx solves the second step. It is a release creation tool, wrapped into a single executable file. It doesn't require a configuration file. And if you do need one, it will be a pretty small one.</i>
And here's a quick excerpt from a user-testimonial for erlang.mk, by Jesper L. Andersen[3]:
When compiling from warm, it takes rebar 9 seconds to figure out that there is nothing to do in the project. erlang.mk does the same thing in 0.2 seconds.
I agree with all the points. Specially with the last one. You can change some ifs and cases by functions. Pattern matching is more idiomatic than the comparisons you do on the if and case headers.
Is using relx in conjunction with rebar a standard thing to do? Is there an easier/standard way to start the app with rebar or relx in development without building an entire release? Having to manually call ensure_all_started while developing seems a bit strange to me.
Good overview of how to get started with Erlang. Sometimes those that give it a first try are the best ones at writing a getting started guide.
> I'm not exactly sure what one_to_one is, but it works. It makes sense to look through Erlang documentation and find out the definition.
one_for_one just means that if one child of the supervisor dies, it will be restarted. There other choice is all_for_one, which means that if one child crashes, supervisor will restart all its children.
You can nest supervisors and create hierarchies of supervisors and workers and restart them as you need. Each process has an isolated heap (just like OS processes) and it will be neatly cleaned up by the garbage collector.
The *app.src file is used to store metadata about the application, and compilation will munge it a bit and copy it into your ebin directory (without the app.src bit I believe).
Essentially, it's what allows application:start(erl2048) to work. The {mod, {erl2048_app, []}} param is what indicates you want to run the code in erl2048_app.erl (though obviously the compiled beam file equivalent) as an application; the application behavior says that starting an application calls the equivalent start/# function.
So basically the order of startup when you call application:start(erl2048) is
Erlang looks for the munged erl2048.app.src, and loads that metadata.
The mod argument specifies a module to start.
The start function in that module is called (as it is expected to adhere to the application behavior). That function should start up the top level supervisor, which in turn is in charge of starting your entire supervisor heirarchy, and by extension, your actual worker processes.
That was a very helpful tutorial. I plan on messin around with the code later tonight. I really am starting to like kukuruku, haven't been able to find content like this lately...
- it might be easier to start your app with `application:ensure_all_started(erl2048)`. If your dependencies are well set in your app file, this will boot all dependencies for you.
- Given you've already compiled, you don't need the `rebar compile skip_deps=true` part to start the app -- that's just to recompile before you boot it, but I guess you knew that already.
- Eventually you get to look in releases, but they're a bit more complex. Look at http://relx.org for an easier way in. Releases will let your system know how to boot apps (so you don't have to `-eval` anything), and lets you pre-configure shell options (`-noshell -detached`) so that you can just boot the server with a `./_rel/bin/my-release` call.
- For information on supervisors and what things like `one_on_one` mean, see http://learnyousomeerlang.com/supervisors
- The naming convention for Erlang functions is `some_function`, not `someFunction`. This makes it easier to visually parse between variables (`SomeVal`) and functions (`some_function`).
- functions like `withinBounds({X, Y})` can be rewritten as `within_bounds({X,Y}) -> (X > 0), (X =< ?SIZE) andalso (Y > 0), (Y =< ?SIZE).` -- no need to pattern match there past the initial tuple. Remove the `_` matching, and just let it crash if something doesn't fit. You'll see it in logs (somewhere) and can fix it later.
- Functions like `getVector(up) -> {-1,0}` could easily be made declarative by using `vector(up) -> {-1,0}` or just `up() -> {-1,0}`.
- Break out large functions more. There's often no reason to have a 100+ lines function in Erlang. If you end up nesting `case` constructs more than two level deeps, start thinking about splitting the functions up and deferring stuff to other function calls. Your code will also be more testable that way, easier to reason about when something goes wrong, and easier to delete and refactor without breaking anything.
wow, that's a lot of useful information. Thank you. I haven't seen relx before. Very good find, thank you.
Another tool you might want to check out is erlang.mk, by the author of the Cowboy webserver, Loïc Hoguin -- from the announcement[1]:
erlang.mk is a rebar replacement. It was initially created for allowing a faster development process than rebar and for better compatibility with Linux build tools. It should work on Linux and OSX with GNU Make installed.
Here's how erlang.mk and relx can be used together to build releases (this is from a post by Loïc)[2]:
There is two steps to building a release. First you need to build the various OTP applications you want to include in the release. Once done, you need to create the release itself, by including the Erlang runtime system alongside the applications, a boot script to start the node and all its applications, and some configuration files.
erlang.mk solves the first step. It is an include file for GNU Make. Just including it in a Makefile is enough to allow building your project, fetching and building dependencies, building documentation, performing static analysis and more.
relx solves the second step. It is a release creation tool, wrapped into a single executable file. It doesn't require a configuration file. And if you do need one, it will be a pretty small one.</i>
And here's a quick excerpt from a user-testimonial for erlang.mk, by Jesper L. Andersen[3]:
When compiling from warm, it takes rebar 9 seconds to figure out that there is nothing to do in the project. erlang.mk does the same thing in 0.2 seconds.
[1] http://erlang.org/pipermail/erlang-questions/2013-August/075...
[2] http://ninenines.eu/articles/erlang.mk-and-relx/
[3] https://medium.com/p/708597c0dd08
I personally still prefer to use rebar. If you want to make building a release with relx part of rebar, use a compile hook:
{post_hooks,[{compile, "./relx"}]}.
in your rebar.config.
I agree with all the points. Specially with the last one. You can change some ifs and cases by functions. Pattern matching is more idiomatic than the comparisons you do on the if and case headers.
Perhaps you meant something like 'within_bounds({X,Y}) when X > 0, X =< ?SIZE, Y > 0, Y =< ?SIZE -> true.' ?
As written the function would return only the value of (Y =< ?SIZE).
I forgot to replace one of the commas with an 'andalso'. There's no reason to have it as a guard. It's too late to edit now, though.
Is using relx in conjunction with rebar a standard thing to do? Is there an easier/standard way to start the app with rebar or relx in development without building an entire release? Having to manually call ensure_all_started while developing seems a bit strange to me.
Very good write up!
Good overview of how to get started with Erlang. Sometimes those that give it a first try are the best ones at writing a getting started guide.
> I'm not exactly sure what one_to_one is, but it works. It makes sense to look through Erlang documentation and find out the definition.
one_for_one just means that if one child of the supervisor dies, it will be restarted. There other choice is all_for_one, which means that if one child crashes, supervisor will restart all its children.
You can nest supervisors and create hierarchies of supervisors and workers and restart them as you need. Each process has an isolated heap (just like OS processes) and it will be neatly cleaned up by the garbage collector.
The *app.src file is used to store metadata about the application, and compilation will munge it a bit and copy it into your ebin directory (without the app.src bit I believe).
Essentially, it's what allows application:start(erl2048) to work. The {mod, {erl2048_app, []}} param is what indicates you want to run the code in erl2048_app.erl (though obviously the compiled beam file equivalent) as an application; the application behavior says that starting an application calls the equivalent start/# function.
So basically the order of startup when you call application:start(erl2048) is
Erlang looks for the munged erl2048.app.src, and loads that metadata.
The mod argument specifies a module to start.
The start function in that module is called (as it is expected to adhere to the application behavior). That function should start up the top level supervisor, which in turn is in charge of starting your entire supervisor heirarchy, and by extension, your actual worker processes.
Some style stuff, for terseness, you can do things like change -
to
ohh, I see. So I can pass Direction as an atom now..that's cool. Thanks!
That was a very helpful tutorial. I plan on messin around with the code later tonight. I really am starting to like kukuruku, haven't been able to find content like this lately...
2048 is the new "Hello world"