erlang tuples to list to maps from a text file - erlang

I am trying to read data from text file put the tuples from the file into a list and then map them the code is as follows I am new to erlang and dont know what I am doing wrong:
-module(exchange).
-export([start/0]).
start()->
A= file:consult("calls.txt"),
B=tuple_to_list(A),
io:fwrite("~p~n",[maps:from_list([B])]).
It gives me the error :
5> c(exchange).
{ok,exchange}
26> exchange:start().
** exception error: bad argument
in function maps:from_list/1
called as maps:from_list([[ok,
[{john,[jill,joe,bob]},
{jill,[bob,joe,bob]},
{sue,[jill,jill,jill,bob,jill]},
{bob,[john]},
{joe,[sue]}]]])
in call from exchange:start/0 (exchange.erl, line 10)
Please help me here . Thank you .

file:consult/2 returns {ok, Terms} on success so this should work:
{ok, A} = file:consult("calls.txt"),
io:fwrite("~p~n", [maps:from_list(A)]).

Related

Can you specify a type for function argument in erlang using apply(M, F, A)?

In the example below, I would like to specify a type for argument in the apply(M, F, A) call, but I can't figure how. Here, dialyzer doesn't complain for type mismatch between {event, "something append !"} and {anyevent, string()} type definition in callback_function spec :
-module(erl_test).
-export([
callback_function/1,
test_it/0
]).
-spec callback_function(
Event::{anyevent, string()}) -> ok.
callback_function(Event) ->
io:format("event received: ~p~n", [Event]),
ok.
-spec notify_something(CbMod::module(), CbFun::atom()) -> ok.
notify_something(CbMod, CbFun) ->
apply(CbMod, CbFun, [{event, "something append !"}]),
ok.
test_it() ->
notify_something(?MODULE, callback_function).
Or do you have any other design proposition that I could use to do type checking on a callback function ?
Thank you !
Using apply/3 as it is, I believe you are out of luck.
However, you could change the line:
apply(CbMod, CbFun, [{event, "something append !"}]),
to:
CbMod:CbFun([{event, "something append !"}]),
This would make Dialyzer aware of the specified argument type.

What is this Elixir message means

I am writing Elixir to get record from remote nodes, I have write a module,
defmodule Connect do
def connect do
node_ap_dev_ejd = :'abc#abc.com'
:net_adm.ping(node_ap)
fbUsersFun = fn(x) -> :binary.part(x,{0,3}) == <<"*ab">> end
f = fn()-> :mnesia.dirty_select(:'cz_map',[{{:cz_map, :'$1', :'_',:'_',:'_'},[],[:'$1']}]) end
fbUserList = :rpc.call(node_ap_dev_ejd,:mnesia,:activity,[:async_dirty,f])
list = Enum.filter(fbUserList ,fbUsersFun)
length(list)
end
end
I can run the code if I put it in iex shell line by line, however if I compile the code and run Connect.connect , this error appear, I have no idea of it, please suggest
** (Protocol.UndefinedError) protocol Enumerable not implemented for
{:badrpc, {:EXIT, {:undef, [{#Function<1.96315226/0 in Connect.connect/0>, [], []}, {:mnesia_tm, :non_transaction, 5, [file: 'mnesia_tm.erl', line: 738]}]}}}
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:112: Enumerable.reduce/3
(elixir) lib/enum.ex:666: Enum.filter/2
second_function.ex:10: Connect.connect/0
It means that the Enumerable protocol is not implemented for the data {:badrpc, ...}.
Most likely, that error comes from this line:
list = Enum.filter(fbUserList ,fbUsersFun)
In that line, you're trying to filter fbUserList which I guess is {:badrpc, ...} instead of an enumerable. Tuples are not enumerables; lists and maps (and other things) are.
The solution probably lies in a case expression which checks the result returned by :rpc.call/4 in order to defend from errors:
case :rpc.call(node_ap_dev_ejd, :mnesia, :activity, [:async_dirty, f]) do
{:badrpc, _} -> raise "bad rpc error"
fbUserList -> Enum.filter(fbUserList, ...) # and so on
end
I'm having the same issue working mnesia with erlang, the error is because of the anonymous function "f", the thing is that the remote node does not recognize that function because it was created in another node.
EDIT:
I managed to solve the problem in erlang, I will show you how I did it in erlang, I don't know much about elixir but I´m sure if it can be done in erlang it will in elixir.
So this segment
f = fn()-> :mnesia.dirty_select(:'cz_map',[{{:cz_map, :'$1', :'_',:'_',:'_'},[],[:'$1']}]) end
fbUserList = :rpc.call(node_ap_dev_ejd,:mnesia,:activity,[:async_dirty,f])
In erlang is like this
f = fun()-> mnesia:dirty_select(cz_map,[{{cz_map, '$1', '_', '_', '_'},[],['$1']}]) end,
fbUserList = rpc:call(node_ap_dev_ejd, mnesia, activity, [async_dirty, f])
Instead declaring an anonymous fun you have to do something like this
fbUserList = rpc:call(node_ap_dev_ejd, mnesia, activity, [async_dirty, mnesia:dirty_select/2, [cz_map, [{{cz_map, '$1', '_', '_', '_'},[],['$1']}]]])
You can find a clear explanation here what kind of types can be sent on an erlang message?
I hope this information helps you.

What am I doing wrong with erl_parse:parse_form?

I wrote a Hello-World module and compiled it successfully. And then I tried to learn the things under the hood by using erl_scan and erl_parse.
-module(useless).
-export([hello/0]).
hello() -> io:format("hello world\n").
and I type in the erl shell
{ok, S} = file:read_file("useless.erl").
and
{ok, T, _} = erl_scan:string(binary_to_list(S)).
It works fine apparently. But when I try erl_parse:parse_form(T).
It gives {error,{2,erl_parse,["syntax error before: ","'-'"]}}
What am I doing wrong?
Edit:
the module compile is helpful.
Also this is cool.
The function erl_parse:parse_form/1 works on one form only. So you must split the result of erl_scan:string/1 into individual forms first.
you can use erl_scan:tokens to achieve this (this code wors, but I am not sure I use the function in the right way):
-module(useless).
-export([eval/1]).
eval(File) ->
{ok, B} = file:read_file(File),
Forms = scan(erl_scan:tokens([],binary_to_list(B),1),[]),
F = fun(X) -> {ok,Y} = erl_parse:parse_form(X), Y end,
[F(X) || X <- Forms].
scan({done,{ok,T,N},S},Res) ->
scan(erl_scan:tokens([],S,N),[T|Res]);
scan(_,Res) ->
lists:reverse(Res).
Robert suggestion is to use the re-entrant feature of erl_scan:tokens/3.
The docs on this function are not explicit and it took me some time to understand that the initial string needed to be closed by eof. (if not the tokens function runs an endless loop).
Here is the code I finally came up with, I hope that will help others.
eval(File)->
{ok, Data} = file:read_file(File),
String=erlang:binary_to_list(Data),
scan(String).
scan(String) when is_list(String)->
scan(String++eof,[]). %% appended eof
scan({done, Result, LeftOverChars},Acc)->
scan_done(Result,LeftOverChars,Acc);
scan({more, Continuation},Acc)->
scan(erl_scan:tokens(Continuation,[],1),Acc);
scan(String,Acc) when is_list(String)->
scan(erl_scan:tokens([],String,1),Acc).
scan_done({error,ErrorMsg,_Location},_LeftOverChars,_Acc)->
ErrorMsg;
scan_done({eof,Location},LeftOverChars,Acc)->
Acc;
scan_done({ok,Tokens,Location},LeftOverChars,Acc)->
case erl_parse:parse_form(Tokens) of
{ok,R}->scan(LeftOverChars,Acc++[R]);
{error,R}->scan(LeftOverChars,R)
end.

Erlang -- How to convert a fun() object to a String

Is there a straightforward way to convert an Erlang fun to a string? A call to io_lib:format only prints the function reference, e.g. something like "#Fun<erl_eval.20.67289768>". For example, I'd like to be able to do this:
1> Fun = fun() -> atom_to_list('hello world') end.
2> FunStr = fun_to_str(Fun).
"fun() -> atom_to_list('hello world') end."
I'm looking for how to implement fun_to_str. In javascript, some interpreters have a .toSource() function that can be called on any object, including functions, that print their string representation. Any info is appreciated, thanks.
First, get the environment variables for the fun (which includes the abstract code):
1> {env, [{_, _, _, Abs}]} = erlang:fun_info(Fun, env).
{env,[{[],
{eval,#Fun<shell.21.83096281>},
{value,#Fun<shell.5.83096281>},
[{clause,1,[],[],
[{call,1,{atom,1,atom_to_list},[{atom,1,hello_world}]}]}]}]}
Pretty print the abstract code using erl_pp:
3> Str = erl_pp:expr({'fun', 1, {clauses, Abs}}).
[[[["fun",
[[[[["()"]," ->"],
["\n ",
[["atom_to_list",[[40,["'hello world'",41]]]]]]]]]]],
[10,["end"]]]]
4> io:format([Str|"\n"]).
fun() ->
atom_to_list('hello world')
end
ok
(You have to add {'fun', 1, {clauses, ...}} around it to make it a complete Erlang expression)
You might be able to use erlang:fun_info/2 for that, atleast i get some information from the shell when doing
1> erlang:fun_info(fun() -> test,ok end, env).
{env,[[],
{value,#Fun<shell.7.37281544>},
{eval,#Fun<shell.24.85590193>},
[{clause,1,[],[],[{atom,1,test},{atom,1,ok}]}]]}
2>
You want the last list with the clause atom and then pretty print it using for instance erl_pp

Thrift/Erlang string

I'm trying to write a simple Thrift server in Erlang that takes a string and returns a string.
Everything seems to be working up to the point of calling my function:
handle_function(Function, Args) when is_atom(Function), is_tuple(Args) ->
case apply(?MODULE, Function, tuple_to_list(Args)) of
ok -> ok;
Reply -> {reply, Reply}
end.
test([X]) ->
"You sent: " ++ X.
I'm getting a function_clause. The stack trace shows the following:
{function_clause, [{server, test,
[<<"w00t">>]},
{server,handle_function, 2}, ...
My handle_function is copied from the tutorial file so I won't be surprised if I need to tweak it. Any ideas?
That last argument of apply should be a list of arguments to 'test', e.g., if tuple_to_list(Args) resulted in:
[1]
...then:
test(1)
If tuple_to_list(Args) resulted in:
[1,2]
...then:
test(1,2)
So, if {<<"woot">>} is being passed to tuple_to_list, that's going to be:
[<<"woot">>]
...so:
test(<<"woot">>)
...but test's signature asks for a list as the argument, so there's a mismatch.

Resources