[erlang-questions] feedback please
zxq9
zxq9@REDACTED
Thu Sep 24 08:45:45 CEST 2015
On Thursday 24 September 2015 08:22:27 Roelof Wobben wrote:
> On question pops into my mind.
>
> You have this code for input the amount :
>
> ask_amount() ->
> Input = io:get_line("How much? "),
> case scrub_amount(Input) of
> {ok, Number} ->
> Number;
> {error, bad_input} ->
> ok = io:format("Hrm... '~tp' does not seem to be a number~n"
> "Now, one more time...~n",
> [Input]),
> ask_amount()
> end.
>
> If I want to check if the input is postive do I need to put a if then
> into the {ok, number} piece.
It depends on where you want to catch it, and this is halfway to being a UI question. Before we had asked the user whether they wanted to deposit or withdraw money. In some really old games this was itself indicated by whether your bank activity was negative or positive. On the other hand, some games have bugs where you can invent money by performing negative deposits or negative withdrawls.
So, first you have to ask yourself whether you want to make it illegal for a user to input a negative amount. If you want this to be illegal, then you want to stop things right here at ask_amount/0 and force the user to input something else. You can use guards in case statements the same way you do in function heads. This provides a simpler solution:
ask_amount() ->
Input = io:get_line("How much? "),
case scrub_amount(Input) of
{ok, Number} when Number > 0 ->
Number;
{ok, 0} ->
ok = io:format("$0? Giving up on the idea already?~n"),
0;
{ok, Number} ->
ok = io:format("Hey, you! ~tp is less than no money!~n",
[Number]),
ask_amount();
{error, bad_input} ->
ok = io:format("Hrm... '~tp' does not seem to be a number~n"
"Now, one more time...~n",
[Input]),
ask_amount()
end.
If you do want to make it legal to act on negative amounts then let it go through, but re-consider your guards in get_balance/2. Not that there is *definitely* some weird case in get_balance/2 now, but almost any time you realize you have an edge case there *very often is* some possible behavior that your program says is correct but is not what you intend. So you have to catch things like this, and stopping it all at the place you validate external input is the best way to do it. Once you accept data into your program you should trust it. Rigorously validate it *once* when it comes in, and from then on trust that the data you are receiving is what you say it is.
Dialyzer can actually help with this quite a bit once you learn how to use it (it pays attention to guards as well as -type definitions). There are cases where it is just hopelessly outmatched by reality, but you can usually collect side effects into a smallish number of functions/modules and type the rest of a program in a way that makes you confident that it will work as intended or crash right away.
Anyway, don't get bogged down in nitpicking this little example program too much. You have bigger fish to fry. You know most of sequential Erlang already (yes, it is a very tiny language). You need to move on to concurrent Erlang and get your feet wet with spawning, linking, monitoring, etc. It will change the way you think about programs, and this is the key benefit you should be getting out of this experience.
-Craig
PS: You can spend days arguing/worrying/paralyzed over input validation, UI details, lists/strings VS binaries, maps vs dicts, records VS tuples, blahblahblahblah. All of that is just background noise. Forget about that stuff for now. Keep moving forward and score some early victories in the arena of concurrency -- these little details will become much less mysterious in the course of writing programs you care about, and by that time the discussions about them will make sense (enough sense that you will develop a sense for which of those discussions to ignore).
More information about the erlang-questions
mailing list