Visualise Erlang

In my experimentation with functional languages such as Scheme
and Erlang, I have found that often (especially when looking at
examples) it is difficult to determine or visualise what an
Erlang Term's type and structure is.

The languages usually give methods to determine the type of
Terms (or variables).These methods are not wholly obvious
to beginners and the Terms may contain in turn other Terms
nested to an arbitrary depth.

What is required is a method of determining recursively
all the Terms in a given Term, without great overheads, and
by a means that does not change the normal operation of the
function who's Terms are being visualised.

The module visualise.erl is designed to meet these needs.
It is used as follows:

import the module into the module being examined

-import(visualise,[itsa/1]).

then call itsa(Term_to_be_examined)


The function itsa/1 returns its argument, i.e.
the above usage of itsa/1 returns the Term_to_be_examined
The side-effect of the function is to output the
visualisation of the Term_to_be_examined on the stderr
console.

The code here includes a test suite for visualise
which tests the operation of the module.

Operation in full is;
copy code to a directory,
cd (change directory) to that directory.
start Erlang

$erl
Erlang (BEAM) emulator version 5.6.2 [source] [64-bit]
[smp:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.6.2 (abort with ^G)
1> c(visualise).
{ok,visualise}
2> visualise:test1().
3> visualise:test().

(full output in Post titled Output of Visualise tests)

Program notes:
itsa/1 calls a subsidiary function it_is_a/2
This function initialises the tabulation feature
which allows recursive tabulated presentation of nested Terms.
Nested Terms can occur in Lists and Tuples and these
recursively call it_is_a/2 passing the degree if indentation
in the second parameter.
This illustrates the use of tail recursion passing
the state ( of indentation ) in the recursive call.

The following Erlang Built in Functions (BIF) are used to determine
what the Terms are.

is_binary/1, is_boolean/1, is_function/1, is_list/1 (includes strings),
is_number/1, is_float/1, is_integer/1, is_tuple/1, is_pid/1, is_port/1,
is_reference/1, is_atom/1.

foreach/2 iterates through the lists and a little function
by Joe Armstrong, for/3 iterates through the tuples.

I may extend this function to iterate through records which
are really only "tuples in disguise".



%% Copyright (C) 2008 David M. W. Martin
%%
%% Redistribution and use in source and binary forms, with or without
%% modification, are permitted provided that the following conditions are met:
%%
%% 1. Redistributions of source code must retain the above copyright notice,
%% this list of conditions and the following disclaimer.
%%
%% 2. Redistributions in binary form must reproduce the above copyright notice,
%% this list of conditions and the following disclaimer in the documentation
%% and/or other materials provided with the distribution.
%%
%% THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
%% FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
%% DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
%% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
%% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
%% OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
%% OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
%% ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-module(visualise).
-import(lists,[foreach/2]).
-export([test/0, test1/0, a_side_effect_function/0, itsa/1]).
-record(user, {id=123, name="david", password="", email="", website=""}).

itsa(Term)->
it_is_a(Term,""). %%start with zero indent ""

it_is_a(Term,N)-> %% sub function to incorporate indenting of output
Tabsize=" ", %%" "
if
is_binary(Term)-> io:format ("~p is a binary of size ~p~n",[Term, size(Term)]),
Term;
is_boolean(Term)-> io:format ("~p is a boolean ~n",[Term]),Term;
is_function(Term)->io:format ("~p is a function ~n",[Term]),Term;

is_list(Term)-> io:format ("~p is list of length ~p~n",[Term, length(Term)]),
%build up tab format here
io:format(N ++ "list comprising of~n"),
foreach(fun(X) ->

io:format(N++ "| " ), it_is_a(X,N ++ "|" ++ Tabsize) end, Term),
io:format(N ++"list end~n"),Term;

is_number(Term)-> io:format ("~p (decimal)is a number",[Term]),
if
is_float(Term)-> io:format (" and a float (approx to 6 digits) ~g~n",[Term]),
Term;
is_integer(Term)->io:format (" and an integer ~.2#, ~.8#, ~.12#~n",
[Term,Term,Term]),Term
end;



is_tuple(Term)-> io:format ("~p is a tuple of size ~p~n",[Term, size(Term)]),
%build up tab format here
io:format(N ++ "tuple comprising of~n"),
for(1,size(Term),fun(X) -> io:format(N ++"| "),
it_is_a(element(X,Term),N ++"|"++Tabsize) end),
io:format(N ++ "tuple end~n"),Term;

is_pid(Term)-> io:format ("~p is a pid ~n",[Term]),Term;
is_port(Term)-> io:format ("~p is a port ~n",[Term]),Term;
is_reference(Term)->io:format ("~p is a reference ~n",[Term]),Term;

is_atom(Term)-> io:format ("~p is an atom ~n",[Term]),Term;
true-> must_be_something_I_forgot
end.


itsa(Term,user)->
if
is_record(Term,user)->io:format ("~p is a record Name ~p~n",[Term,user]),
{Term,user};
true-> not_ok
end.


%%------------------------------ %test suite for visualise ---------------------

test1()->
itsa([start, {here, [is, { 3.141592,999 },true,false], ball}, to_you]).

test()->
Fun1=fun()->1 end,
X=#user{},
itsa("string"),%%list
itsa(<<1,2,17>>),%% is binary
itsa(false),%% is boolean
itsa(3.1415),%% is float
itsa(33333333.1415926535),%% is float
itsa(Fun1),%% is function
itsa(1),%% is integer
itsa(spawn(visualise,a_side_effect_function,[])),%%is pid
itsa(self()),%%is pid
itsa([k,8,""]),%% is list
itsa(make_ref()),%% is a reference
itsa({a,9,[<<7,9>>,"string",true,3.142,Fun1,2,self(),
{here,[this,[that,[here,list,deep]]]}]}),%% is a tuple

itsa({a,9,[<<7,9>>,"string",true,3.142,Fun1,2,self(),
{here,[this,[that,[here,list,deep]]]}]}),%% is a tuple
itsa(X,user).%% is a record Name(must be literal atom for Guard)
%-------------------------------------------------------------------------------
%% a side effect test function
a_side_effect_function()->
io:format ("is a_side_effect_function of a process~n",[]).

%%for (min,max,perform function F)
for(Max,Max,F)->[F(Max)];
for(I,Max,F)->[F(I)|for(I+1, Max, F)].

Comments

Popular posts from this blog

dmesg of first octacore Ubuntu Meizu MX4 ARM Phone

Shootout between 64-bit and 32-bit Intel Atom 230 in D945GCLF Put in perspective with my 2x AMD Athlon(tm) 64 X2 Dual Core Processor 3600+ (Brisbane overclocked to 2736MHz) (all other figures from hardinfo)

Output of Visualise tests