Erlang/OTP Forums

Author Message

<  Erlang  ~  Conflicting behaviors warning...

daserlang
Posted: Thu Dec 11, 2008 12:51 am Reply with quote
Joined: 11 Dec 2008 Posts: 4
Hi. I'd like to make a simple, terminal-based stock ticker. Very basic stuff here. However, in order to be more comfortable with the different behaviours (application, supervisor and gen_server), I've decided to make this an Erlang app (not very impressive at first, but I'm a beginner and simply want to become more familiar).

I envisioned there being two processes. One process was the supervisor and gen_server. It would print out the latest price of the stock also, there would be a second process from another module that I'm writing that would actually do the communication portion to a server and read out the information.

Here is my code:
Code:
% ticker.erl:

-module(ticker).
-behaviour(gen_server).
-behaviour(supervisor).
-behaviour(application).

% needs to be removed in the future, presently only for debugging purposes.
-compile(export_all).

-export([init/1, code_change/3, handle_call/3, handle_cast/2, handle_info/2,
  terminate/2, start/0, start/2, stop/1]).

% start off here. We'll create a new method and register it to an atom.
start() ->
  start(ticker, [?MODULE]).

start(Name, Mod) ->
  register(Name, spawn(fun() ->
                           loop(Name, Mod, Mod:init())
                           end)).

init([]) ->
  % note we must set trap_exit = true if we want terminate/2 to be called when
  %   the application is stopped.
  process_flag(trap_exit, true),
  io:format("~p starting~n", [?MODULE]),
  {ok, {{one_for_one, 3, 10},
    [{tag1, {area_server, start_link, []}, permanent, 10000, worker, [area_server]}]}}.

% this is an in-between function. It's job is to create a custom tuple, to be
%   passed down to the rpc method, which will send a message to a process
%   asking it to hot-swap code.
swap_code(Name, Mod) ->
  rpc(Name, {swap_code, Mod}).

print_output(Stock, Price) ->
  io:format("Stock: ~s Current Price ~w ~n", [Stock, Price]).

% all this thing does is send a message to a process and waiting for a reply.
rpc(Name, Request) ->
  Name ! {self(), Request},
  receive
    {Name, Response} ->
      Response
    end.

% this is the function through which we will constantly iterate, it'll be the
%   heart of this process.
loop(Name, Mod, OldState) ->
  receive
    {From, {swap_code, NewCallBackMod}} ->
      % if received request to swap code, then simply send back a response
      %   saying that you did so and start using the new module.
      From ! {Name, ack},
      % What makes this especially cool is that we can continue to use all of
      %   the properties of the old module (dictionary, etc.), but we gain
      %   (lose) new functions without affecting the said properties. So the
      %   values in the dictionary stay the same.
      loop(Name, NewCallBackMod, OldState);
    {UnprocessedMessage} ->
      io:format("~n~n******* UNPROCESSED MESSAGE *******~n ~s ~n~n",
        [UnprocessedMessage]);
    % the only reason why this message is here is so that the stupid warning
    %   message about Mod will go away. It's necessary to have it for the
    %   moment, but there's just no work-around without making drastic code
    %   changes.
    {change_stock_price, Stock, Price} ->
      Mod:print_output(Stock, Price)
    end.

% putting _ in front of a variable, gets rid of an annoying warning about the
%   variable being unused.
code_change(_OldVsn, N, _Extra) ->
  {ok, N}.

handle_call(_SomeVal, _From, _N) ->
  null.

handle_cast(_Msg, N) ->
  {noreply, N}.

handle_info(_Info, N) ->
  {noreply, N}.

terminate(Reason, _N) ->
  io:format("~p terminating...~n", [?MODULE]),
  io:format("The reason for the hault is: ~p ~n", [Reason]).

stop(State) ->
  io:format("~p stopping...~n", [?MODULE]),
  io:format("The state of the application is: ~p ~n", [State]).


Code:
% net_int.erl: Reads the stock price from somewhere...

-module(net_int).

start() ->
  null.


I'm nowhere near finished, as you can see. However, when I tried compiling ticker.erl, I got this bizarre warning and I'm not sure how to get rid of it. Is an erlang process capable of being either a supervisor or gen_server? If yes, then it seems that I would need to go back to the proverbial drawing boad and re-think how I would implement this.

Error:
Code:
20> c(ticker).
./ticker.erl:5: Warning: conflicting behaviours - callback init/1 required by both 'supervisor' and 'gen_server' (line 4)
{ok,ticker}
View user's profile Send private message
daserlang
Posted: Thu Dec 11, 2008 1:32 am Reply with quote
Joined: 11 Dec 2008 Posts: 4
One possibility is to further modularize this whole thing and create a 3rd process. This one will be the application that binds them all and a supervisor that will look over both processes. But honestly, I'd really like an answer my earlier question as to why I'm getting that warning.

Code:
            +-----------+
            |Application|
      +-----|Supervisor |-----+
      |     +-----------+     |
      V                       V
+----------+             +-------+
|Gen Server| <---------> |net int|
+----------+             +-------+
View user's profile Send private message
klaar
Posted: Thu Dec 11, 2008 1:32 pm Reply with quote
User Joined: 06 Oct 2008 Posts: 11 Location: Göteborg/Sweden
You are getting this error because you are implementing two behaviors which has callbacks with the same name and arity and different return values.

Therefore you could return a supervisor specification as the state to a gen_server, and the state of a gen_server as the supervisor specification.

The root cause of this is that you are using a module as a means to achieve low cohesion when high cohesion would be more desirable.
View user's profile Send private message
daserlang
Posted: Fri Dec 12, 2008 12:01 am Reply with quote
Joined: 11 Dec 2008 Posts: 4
klaar wrote:
You are getting this error because you are implementing two behaviors which has callbacks with the same name and arity and different return values.

Therefore you could return a supervisor specification as the state to a gen_server, and the state of a gen_server as the supervisor specification.

The root cause of this is that you are using a module as a means to achieve low cohesion when high cohesion would be more desirable.

I see your point. After giving it some thought, what you propose makes more sense.
View user's profile Send private message

Display posts from previous:  

All times are GMT
Page 1 of 1
This forum is locked: you cannot post, reply to, or edit topics.

Jump to:  

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum