functional-programmingerlangpattern-matching

Atom pattern matching in an overloaded function head


Brief description

I'm making an Air Pollution Monitor system, where you can add various stations, readings, and then read this saved data afterwards.

Problem

I have an issue while trying to match various atoms within a single function. My code is:

handleRequest(add_station, {Name, Coordinates}, Monitor) ->
  {add_station(Name, Coordinates, Monitor), ok}.

handleRequest(get_station, {Station}, Monitor) ->
  {StationData, _} = get_station(Station, Monitor),
  {Monitor, StationData}.

But it fails to compile under this error: function handleRequest/3 already defined

Usage

I want to call this function like this:

serverLoop(Monitor) ->
  receive
    {request, Pid, {Action, Payload}} ->
      UpdatedMonitorOrError = handleRequest(Action, Payload, Monitor),
      if
        is_tuple(UpdatedMonitorOrError) ->
          % is a tuple like {error, Message} only in case of error occured, is a list otherwise
          Pid ! {reply, UpdatedMonitorOrError},
          serverLoop(Monitor);
        true ->
          % UpdatedMonitorOrError is a valid Monitor which could be treated as the next state
          Pid ! {reply, Response},
          serverLoop(UpdatedMonitorOrError)
      end
    end.

Where Action is a certain atom. I want to avoid making multiple receive pattern matching clauses as most of the code would be repeated.

Note that Payload could be a tuple consisted of various number of elements based on the Action atom and this atom should determine the behavior of handleRequest function. Additionally, Response could be a single atom or a data returned from a function.

Other tries

What I have also tried, to no avail:

handleRequest(add_station, Payload, Monitor) ->
  {Name, Coordinates} = Payload,
  {add_station(Name, Coordinates, Monitor), ok}.

handleRequest(get_station, Payload, Monitor) ->
  {Station} = Payload,
  {StationData, _} = get_station(Station, Monitor),
  {Monitor, StationData}.

What finally worked

handleRequest(Action, Payload, Monitor) ->
  case Action of
    add_station ->
      {Name, Coordinates} = Payload,
      {add_station(Name, Coordinates, Monitor), ok};
    get_station ->
      {Station} = Payload,
      {StationData, _} = get_station(Station, Monitor),
      {Monitor, StationData}
  end.

Two questions

  1. Why is this atom pattern matching not working as intended?
  2. Is this Erlang-way of solving this problem? Is there a better approach? Note that an action could have invalid Payload and then it should return an error and don't modify stored data.

Solution

  • In Erlang, clauses of the same function must be separated by a semicolon instead of a dot:

    handleRequest(add_station, {Name, Coordinates}, Monitor) ->
      {add_station(Name, Coordinates, Monitor), ok};
                                               %%  ↑ note ";" instead of "."
    handleRequest(get_station, {Station}, Monitor) ->
      {StationData, _} = get_station(Station, Monitor),
      {Monitor, StationData}.