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.
Related
I'm using elixir and the httpc erlang module to ping a website. I can hit the website just fine but when I try to use status_code I get an argument error. I can see that the data is there in the error but I'm not sure why the error is being thrown.
What arguments am I supposed to be providing? I thought that this function only returned an integer but it looks like it's expecting something.
:inets.start
{:ok, result} = :httpc.request(:get, {'http://www.erlang.org', []}, [], [])
result.status_code()
** (ArgumentError) argument error
:erlang.apply({'HTTP/1.1', 200, 'OK'}, :status_code, [])
:httpc.request returns tuple which contains various information. The status code can be retrieved as follows.
:inets.start
{:ok, result} = :httpc.request(:get, {'http://www.erlang.org', []}, [], [])
{status_line, headers, body} = result
{http_version, status_code, reason_phrase} = status_line
The tuple structure is described as in the httpc doc. (http://erlang.org/doc/man/httpc.html#request-4)
If you want to use more elixir-style syntax (like result.status_code), one option would be to use some of the elixir libraries like httpoison or httpotion.
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.
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
I want to call xyz with the name of a function to be invoked.
-module(sample).
-export([xyz/1]).
xyz(Name) -> Name().
p() -> "you called p".
g() -> "you called g".
But I get the following error:
1> c(sample.erl).
./sample.erl:6: Warning: function p/0 is unused
./sample.erl:7: Warning: function g/0 is unused
{ok,sample}
2> sample:xyz('p').
** exception error: bad function p
in function sample:xyz/1
3>
It is correct that you have to export p and g. You can then use apply/3 to call it.
erlang:apply(sample, p, []).
Only fun-values are usable with the Fun(...) syntax. You are passing in an atom-value. An atom is a 'bad function' as the error message go. You could do something similar to
xyz(p) -> fun p/0;
xyz(g) -> fun g/0.
Then go ahead and call
Fun = xyz(p),
Fun()
-module(sample).
-export([xyz/1, p/0, g/0]).
xyz(Name) -> ?MODULE:Name().
p() -> "you called p".
g() -> "you called g".
1> sample:xyz(p).
"you called p"
Pattern match is the idiom to use:
-module(sample).
-export([xyz/1]).
xyz(p) -> p();
xyz(q) -> g().
p() -> "you called p".
g() -> "you called g".
If you want to be dynamic you can use a gen_event server.
Essentially what this is is a server that holds a state which consists of key/function pair like so:
[{p, #func1},
{g, #func2},
{..., ...},
...]
You can then essentially bind events to functions. (there is, needless to say, a bit more to it than that.
The easiest way to do is to try exporting p and g along with xyz.
-export([xyz/1, p/0,g/0]).
After exporting the function p and g can be called as follows :
1> sample:xyz(fun sample:p/0).
"you called p"
2> sample:xyz(fun sample:g/0).
"you called g"
Another way to look at it is that (depending on the problem you're solving) dynamic calls to functions isn't necessarily the right approach. Given that processes and message passing are the way you organize your code in Erlang since it's a "concurrency oriented language", maybe you could just use message passing with a selective receive rather than mimicking idioms of a sequential language? Send a message for what you want and get the custom reply based on that. It's about the result of each function, not the function itself, after all. (Plus there's the flexibility and scalability of message passing, etc.)
Although processes aren't totally free compared to calling from a library module, Erlang-level processes are dirt cheap (especially if the message communication is within the same node). They're not OS-level processes. The overhead would be comparable (or better) to dynamic function calls and object instantiation in heavier scripting languages.
The following code gives me an error: "syntax error before: Some_ets"
-module(tut).
-export([incr/1]).
Some_ets = ets:new(?MODULE, [bag]).
incr(X) ->
X+1.
But I am able to declare the ETS within a function, like:
-module(tut).
-export([incr/1]).
incr(X) ->
Some_ets = ets:new(?MODULE, [bag]),
X+1.
Can't I declare a ETS outside a function?
No - unlike other languages there isn't a concept of static initialization - there's no appropriate time for an Erlang system to execute that piece of code.
Erlang does have the concept of a parameterized module however, and that may be what you're after. Have a look here http://www.lshift.net/blog/2008/05/18/late-binding-with-erlang which is a good write up of that - it would allow you to instantiate an "instance" of your tut module bound to a given ets table and save passing around that handle explicitly in your module function calls.
Or if you are into OTP you could have the handle to the ets table passed around in the state variable:
init(_) ->
Some_ets = ets:new(?MODULE, [bag]),
{ok, Some_ets}.
and then use it in your handle_call methods:
get_ets_handle() ->
gen_server:call(?MODULE, {getETSHandle}, infinity).
handle_call({getETSHandle}, _From, Some_ets) ->
{reply, Some_ets, Some_ets}.
You can't do variable assignments like that in a module. See here.