5

Before I start, I have already checked out Handle badarg in Erlang But I am still not successful in my undefined checks so I removed them.

I am building a dummy bank process and when a client does a balance query check to the bank process, the program exits, saying:

Error in process <0.373.0> with exit value:
{badarg,[{project4,client,3,
                   [{file,"/Users/owner/Desktop/bank.erl"},
                    {line,27}]}]}

=ERROR REPORT==== 26-Oct-2016::13:34:57 ===
Error in process <0.379.0> with exit value:
{badarg,[{project4,client,3,
                   [{file,"/Users/owner/Desktop/bank.erl"},
                    {line,27}]}]}

=ERROR REPORT==== 26-Oct-2016::13:34:57 ===
Error in process <0.375.0> with exit value:
{badarg,[{project4,client,3,
                   [{file,"/Users/owner/Desktop/bank.erl"},
                    {line,27}]}]}

=ERROR REPORT==== 26-Oct-2016::13:34:58 ===
Error in process <0.377.0> with exit value:
{badarg,[{project4,client,3,
                   [{file,"/Users/owner/Desktop/bank.erl"},
                    {line,27}]}]}
<0.378.0> Balance request: 54> 
=ERROR REPORT==== 26-Oct-2016::13:34:58 ===
Error in process <0.378.0> with exit value:
{badarg,[{project4,client,3,
                   [{file,"/Users/owner/Desktop/bank.erl"},
                    {line,39}]}]}
<0.372.0> Balance request: 54> 
=ERROR REPORT==== 26-Oct-2016::13:34:58 ===
Error in process <0.372.0> with exit value:
{badarg,[{project4,client,3,
                   [{file,"/Users/owner/Desktop/bank.erl"},
                    {line,39}]}]}

The code for the simulation is here:

-module(bankSim).
-export([start/0, negativeOrPositive/0, sleep/1, generate_rand_int_list/3, generate_rand/2, client/3, clientSpawn/2, bank/2]).

negativeOrPositive() -> 
    M = rand:uniform(),
    if
        M =< 0.5 -> 
            1;
        true -> 
            -1
    end.

sleep(T) ->
    receive
        after T -> ok
    end.

generate_rand_int_list(N,StartVal,Lim) ->
    lists:map(fun (_) -> (rand:uniform(Lim-StartVal) + StartVal)* negativeOrPositive() end, lists:seq(1,N)).

generate_rand(StartVal, Lim) ->
    rand:uniform(Lim-StartVal) + StartVal.

client([], _ , BankID) ->
    BankID ! {goodbye};
client([H|T], Count, BankID) ->
    BankID ! {transaction, self(), H},
    receive
        {Amount, Balance, Success} ->
            io:format("Client: ~w, Amount requested: ~w, Bank Balance: ~w, Transaction successful ~w ~n",[self(), Amount, Balance, Success]);
        { X } ->
            io:format("The balance is ~w ~n", [X])
    end,    
    sleep(generate_rand(500, 1500)),
    Mod = Count rem 5,
    if 
        Mod == 0 -> 
            io:format("~w Balance request: ",[ self() ]),
            BankID! {balance, self()};
        true -> 
            ok
    end,
    client(T, Count + 1, BankID).

clientSpawn(0, _) ->
        io:format("Finished spawning clients ~n",[]);
clientSpawn(N, BankID) -> 
        spawn(bankSim, client, [ generate_rand_int_list( generate_rand(10, 20), 0, 100) , 1, BankID] ),
        clientSpawn(N-1, BankID).


bank(Balance, 0) ->
    io:format("Banking simulation ended with a final balance of ~w ~n", [Balance]),
    io:format("simulation completed ~n", []);
bank(Balance, NumClients) ->
    receive
        {balance, Client} ->
            Client ! {Balance};
        {transaction, Client, Amount} ->
            NewBalance = Balance + Amount,
            if 
                NewBalance =< 0 ->
                    Client ! {Amount, NewBalance, no},
                    bank(Balance, NumClients);
                NewBalance > 0 ->
                    Client ! {Amount, NewBalance, yes},
                    bank(NewBalance, NumClients);
                true -> 
                    io:format("This will never be printed")
            end;
        goodbye ->
            NewNumClients = NumClients - 1,
            bank(Balance, NewNumClients)
    end.


start()->
    N = generate_rand(2,10),
    register(bank ,spawn(bankSim, bank, [generate_rand(2000,3000), N])),
    clientSpawn(N,bank).

Any help would be appreciated.

Community
  • 1
  • 1
Q.H.
  • 1,406
  • 2
  • 17
  • 33
  • 3
    Looks like you forgot the recursive call for `{balance, Client}` message? (Line 57-58.) – Dogbert Oct 26 '16 at 17:47
  • That seemed to have worked, I've been staring at it so long I've probably kept glossing over such a simple mistake. – Q.H. Oct 26 '16 at 21:04

1 Answers1

13

The Erlang documentation will tell you that badarg means "The argument is of wrong data type, or is otherwise badly formed." Which I never find very elucidating.

Dogbert points out that you missed a recursive call to bank/2 in one of your cases. This is the source of your badargs -- but why?

I always think of badarg as meaning "bad argument sent to a BIF". If you're calling a regular Erlang function, you'll get a function_clause error, meaning that there was no function clause that matched your data.

The error message you're getting lists two different line numbers. One is for the line

BankID ! {transaction, self(), H},

and the other for

BankID! {balance, self()};

The argument that is bad is BankID. The reason that it's bad is because it is an atom, bank. In your start/0 function, you spawn a new bank process and then register the PID of that process as bank. Then in your client, you're sending messages to the bank atom and having Erlang resolve that to the registered PID.

It works for a while, but when you hit the missing-recusion case your bank process terminates. Now there's nobody registered behind the bank atom, and the send operator blows up because it can't send to a defunct name.

If you changed you code to

Bank = spawn(bankSim, bank, [generate_rand(2000,3000), N]),
clientSpawn(N, Bank).

then you would not get the badarg, because it's not an error to send messages to a PID where nobody's running. You might enjoy trying that without fixing the bank loop and seeing what happens.

Nathaniel Waisbrot
  • 23,261
  • 7
  • 71
  • 99
  • I'm marking this as correct because of your explanation and the solution you offered is correct as well. Thank you for your insight! – Q.H. Oct 26 '16 at 21:06
  • 1
    @Nathaniel, a remark about the sentence `I always think of badarg as meaning "bad argument sent to a BIF"`, `To ! Msg` is a call to the operator `send` which uses 2 parameters, although the Erlang creators chose an operator syntax to send messages, there is not much difference compare to `global:send(To,Msg)` syntax. very neat answer. – Pascal Oct 27 '16 at 07:21
  • You will also find that in some of the standard libraries you will also get a `badarg` error if the arguments are wrong. This is explicitly done as it is definitely more informative than a `function_clause` error, or `case_clause`, or some other error that may occur because of a bad argument. – rvirding Nov 16 '16 at 11:47