Solve Erlang Badmatch Error Assigning Lists to Records

I’m trying to assign the ConnectedAtoms list to the connectedatoms field inside a newly created server_st record, but I keep running into a badmatch error. Here’s the setup: I’m using a loop/2 function to check if an Atom is already connected by calling funcs:hasElem on St#server_st.connectedatoms. If it’s not connected, I want to update connectedatoms by appending the new Atom to it. However, when I attempt to create the new St with #server_st{servername=St#server_st.servername,connectedatoms=ConnectedAtoms}, Erlang throws a badmatch error. I suspect the issue lies in how I’m handling the assignment, but I can’t pinpoint what’s going wrong here.

Error Code:

code-module(server). 
-export([loop/2, initial_state/1]).

-include_lib("./defs.hrl").


loop(St, {tryConnect, Atom}) ->
IsConnected = funcs:hasElem(St#server_st.connectedatoms, Atom),
if
IsConnected == true ->
{'EXIT', user_already_connected};
IsConnected == false ->
ConnectedAtoms = St#server_st.connectedatoms ++ [Atom],
St = #server_st{servername = St#server_st.servername, connectedatoms = ConnectedAtoms},
{"Connected!", St}
end;

loop(St, _Msg) ->
io:format("Server got message.~n"),
{ok, St}.


initial_state(_Server) ->
#server_st{servername = _Server, connectedatoms = []}.

The badmatch error occurs because Erlang variables are immutable, meaning you cannot reassign St after it has been defined. To handle this correctly, we need to create a new variable for the updated state rather than trying to reassign St.

Solution:

  1. Checking Connection Status: We use the function funcs:hasElem/2 to check if Atom already exists in St#server_st.connectedatoms.
  2. Updating the ConnectedAtoms List: If Atom is not connected (IsConnected == false), we create a new list by appending Atom to St#server_st.connectedatoms.
  3. Creating a New State Record: We then create a new record (e.g., NewSt) instead of attempting to reassign St.
  4. Returning the Updated State: We return the new state NewSt to complete the update without reassigning St.

Correct code:

code-module(server).
-export([loop/2, initial_state/1]).

-include_lib("./defs.hrl").

loop(St, {tryConnect, Atom}) ->
IsConnected = funcs:hasElem(St#server_st.connectedatoms, Atom),
if
IsConnected == true ->
{'EXIT', user_already_connected};
IsConnected == false ->
ConnectedAtoms = St#server_st.connectedatoms ++ [Atom],
NewSt = #server_st{servername = St#server_st.servername, connectedatoms = ConnectedAtoms},
{"Connected!", NewSt}
end;

loop(St, _Msg) ->
io:format("Server got message.~n"),
{ok, St}.

initial_state(_Server) ->
#server_st{servername = _Server, connectedatoms = []}.

Explanation of Changes:

  • New Variable (NewSt): Instead of reassigning St, we create NewSt to hold the updated state after adding Atom to connectedatoms.
  • Returning NewSt: In the if clause where IsConnected == false, we return {"Connected!", NewSt} to represent the updated state.

This code will now run without the badmatch error because St is not being reassigned. Instead, a new state (NewSt) is created and returned, keeping with Erlang’s immutability requirements.

Final Thought:

The key to resolving the badmatch error in Erlang lies in understanding its immutability constraints. By avoiding variable reassignment and creating a new state record, we respect Erlang’s functional nature and maintain cleaner, error-free code. This approach not only fixes the error but also reinforces best practices in handling state updates in Erlang, especially within record-based structures. Embracing these principles leads to more robust, predictable, and maintainable code in the long run.

Related blog posts