Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Erlang record item list

For example i have erlang record:

-record(state, {clients
            }).

Can i make from clients field list?

That I could keep in client filed as in normal list? And how can i add some values in this list?

Thank you.

like image 972
0xAX Avatar asked Feb 10 '11 05:02

0xAX


2 Answers

Maybe you mean something like:

-module(reclist).
-export([empty_state/0, some_state/0, 
         add_client/1, del_client/1,
         get_clients/1]).

-record(state, 
     {    
          clients = []   ::[pos_integer()],
          dbname         ::char()
     }).    

empty_state() ->
     #state{}.

some_state() ->
     #state{
          clients = [1,2,3],
          dbname  = "QA"}.

del_client(Client) ->
     S = some_state(),
     C = S#state.clients,
     S#state{clients = lists:delete(Client, C)}. 

add_client(Client) ->
     S = some_state(),
     C = S#state.clients,
     S#state{clients = [Client|C]}.

get_clients(#state{clients = C, dbname = _D}) ->
     C.

Test:

1> reclist:empty_state().
{state,[],undefined}
2> reclist:some_state(). 
{state,[1,2,3],"QA"}
3> reclist:add_client(4).
{state,[4,1,2,3],"QA"}
4> reclist:del_client(2).
{state,[1,3],"QA"}

::[pos_integer()] means that the type of the field is a list of positive integer values, starting from 1; it's the hint for the analysis tool dialyzer, when it performs type checking.

Erlang also allows you use pattern matching on records:

5> reclist:get_clients(reclist:some_state()).
[1,2,3]

Further reading:

  • Records
  • Types and Function Specifications
  • dialyzer(1)

@JUST MY correct OPINION's answer made me remember that I love how Haskell goes about getting the values of the fields in the data type.

Here's a definition of a data type, stolen from Learn You a Haskell for Great Good!, which leverages record syntax:

data Car = Car {company :: String 
               ,model   :: String
               ,year    :: Int
               } deriving (Show)

It creates functions company, model and year, that lookup fields in the data type. We first make a new car:

ghci> Car "Toyota" "Supra" 2005
Car {company = "Toyota", model = "Supra", year = 2005}

Or, using record syntax (the order of fields doesn't matter):

ghci> Car {model = "Supra", year = 2005, company = "Toyota"}
Car {company = "Toyota", model = "Supra", year = 2005}
ghci> let supra = Car {model = "Supra", year = 2005, company = "Toyota"}
ghci> year supra
2005

We can even use pattern matching:

ghci> let (Car {company = c, model = m, year = y}) = supra
ghci> "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
"This Toyota Supra was made in 2005"

I remember there were attempts to implement something similar to Haskell's record syntax in Erlang, but not sure if they were successful.

Some posts, concerning these attempts:

  • In Response to "What Sucks About Erlang"
  • Geeking out with Lisp Flavoured Erlang. However I would ignore parameterized modules here.

It seems that LFE uses macros, which are similar to what provides Scheme (Racket, for instance), when you want to create a new value of some structure:

> (define-struct car (company model year))
> (define supra (make-car "Toyota" "Supra" 2005))
> (car-model supra)
"Supra"

I hope we'll have something close to Haskell record syntax in the future, that would be really practically useful and handy.

like image 70
YasirA Avatar answered Sep 18 '22 10:09

YasirA


Yasir's answer is the correct one, but I'm going to show you WHY it works the way it works so you can understand records a bit better.

Records in Erlang are a hack (and a pretty ugly one). Using the record definition from Yasir's answer...

-record(state, 
     {    
          clients = []   ::[pos_integer()],
          dbname         ::char()
     }).

...when you instantiate this with #state{} (as Yasir did in empty_state/0 function), what you really get back is this:

{state, [], undefined}

That is to say your "record" is just a tuple tagged with the name of the record (state in this case) followed by the record's contents. Inside BEAM itself there is no record. It's just another tuple with Erlang data types contained within it. This is the key to understanding how things work (and the limitations of records to boot).

Now when Yasir did this...

add_client(Client) ->
     S = some_state(),
     C = S#state.clients,
     S#state{clients = [Client|C]}.

...the S#state.clients bit translates into code internally that looks like element(2,S). You're using, in other words, standard tuple manipulation functions. S#state.clients is just a symbolic way of saying the same thing, but in a way that lets you know what element 2 actually is. It's syntactic saccharine that's an improvement over keeping track of individual fields in your tuples in an error-prone way.

Now for that last S#state{clients = [Client|C]} bit, I'm not absolutely positive as to what code is generated behind the scenes, but it is likely just straightforward stuff that does the equivalent of {state, [Client|C], element(3,S)}. It:

  • tags a new tuple with the name of the record (provided as #state),
  • copies the elements from S (dictated by the S# portion),
  • except for the clients piece overridden by {clients = [Client|C]}.

All of this magic is done via a preprocessing hack behind the scenes.

Understanding how records work behind the scenes is beneficial both for understanding code written using records as well as for understanding how to use them yourself (not to mention understanding why things that seem to "make sense" don't work with records -- because they don't actually exist down in the abstract machine...yet).

like image 33
JUST MY correct OPINION Avatar answered Sep 19 '22 10:09

JUST MY correct OPINION