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.
Related
I have a function that intentionally throws when its first argument is the atom throw.
A simplified version of this code is:
-module(sample).
-export([main/1, throw_or_ok/1]).
main(_Args) ->
throw_or_ok(throw).
throw_or_ok(Action) ->
case Action of
throw -> throw("throwing");
ok -> ok
end.
Dialyzer errors on the call to throw_or_ok:
sample.erl:7: The call sample:throw_or_ok
('throw') will never return since it differs in the 1st argument from the success typing arguments:
('ok')
Adding specs doesn't help, the error message is the same:
-module(sample).
-export([main/1, throw_or_ok/1]).
-spec main(_) -> no_return().
main(_Args) ->
throw_or_ok(throw).
-spec throw_or_ok(throw) -> no_return(); (ok) -> ok.
throw_or_ok(Action) ->
case Action of
throw -> throw("throwing");
ok -> ok
end.
How can I get Dialyzer to accept calls to throw_or_ok/1 that are guaranteed to throw?
Unfortunately there is currently no clean way to mark this as acceptable for Dialyzer via specs.
Perhaps you can use an ignore warning annotation, however.
Looks like if will put throw it will never return, if will put ok the pattern will never match with throw. See topic with similar issue. The logic of main/1 need to change, eg:
main(Args) ->
MaybeOk = case Args of
0 -> throw;
_ -> ok
end,
throw_or_ok(MaybeOk).
OR
main(_Args) ->
throw_or_ok(_Args).
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.
It's my first attempt to write anything in Erlang, so maybe the question is silly.
I'm writing a quite simple HTTP server using cowboy
db_name() -> "DB_test".
timestamp() ->
calendar:datetime_to_gregorian_seconds(calendar:universal_time()).
sha(Str) ->
<<X:256/big-unsigned-integer>> = crypto:hash(sha256, Str),
lists:flatten(io_lib:format("~64.16.0b", [X])).
handle_post(Req0, State) ->
Link = binary_to_list(cowboy_req:header(<<"link">>, Req0)),
dets:open_file(db_name(), []),
dets:insert(db_name(), {hashed_url(Link), Link, timestamp()}),
Req = cowboy_req:reply(200,
#{<<"content-type">> => <<"text/plain">>},
sha(Link),
Req0),
{ok, Req, State}.
The idea is that a POST HTTP request contains a 'link' header with some link. After recieving such request my server should store it's hash in dets table along with the link and its timestamp. The problem is that the "DB_test" file isn't created. Why?
Based on your example code, it's hard to say exactly why, since you're ignoring the return values from both dets:open_file/2 and dets:insert/2.
Both of them return different values for the success and failure cases; but do not throw exceptions.
See the official documentation for more details: http://erlang.org/doc/man/dets.html
The simplest solution to this is to crash the cowboy connection handling process in non-success cases. You can do that by doing something like the following:
{ok, Ref} = dets:open_file(db_name(), []),
ok = dets:insert(Ref, {hashed_url(Link), Link, timestamp()}),
This will crash with a badmatch exception in the failure cases, when the value returned cannot be pattern matched to the left-hand side of the statement, subsequently causing cowboy to return HTTP 500 to the client.
You'll then see details on what the actual error was in the stacktrace logged
A second solution would be to explicitly handle the failure cases, you can use the 'case' keyword for that.
An example would be something like:
case dets:open_file(db_name(), []) of
{ok, Ref} ->
do_success_things();
{error, Reason}=E ->
io:format("Unable to open database file: ~p~n", [E]),
do_failure_things();
end
For further reading, I'd highly recommend the Syntax in functions and Errors and exceptions chapters of Learn you some Erlang for great good: http://learnyousomeerlang.com/
In erlang doc:
return_to Used in conjunction with the call trace flag. Trace the
actual return from a traced function back to its caller. Only works
for functions traced with the local option to erlang:trace_pattern/3.
The semantics is that a trace message is sent when a call traced
function actually returns, that is, when a chain of tail recursive
calls is ended. There will be only one trace message sent per chain of
tail recursive calls, why the properties of tail recursiveness for
function calls are kept while tracing with this flag. Using call and
return_to trace together makes it possible to know exactly in which
function a process executes at any time.
To get trace messages containing return values from functions, use the
{return_trace} match_spec action instead.
Message tags: return_to.
What is "actual return"? I tested a tail recursive function, but it seems no difference.
Following is my test.erl:
-module(test).
-compile(export_all).
-record(state, {stacks=[]}).
test_tracer() ->
Tracer = spawn_tracer(),
erlang:trace_pattern({?MODULE, '_', '_'}, [], [local]),
erlang:trace(self(), true, [call,
arity,
return_to,
%% procs,
%% running,
timestamp,
{tracer, Tracer}]),
loop(100),
Tracer!dump.
spawn_tracer() ->
spawn(fun()-> trace_listener(#state{}) end).
trace_listener(State) ->
receive
dump ->
io:format("~p", [lists:reverse(State#state.stacks)]);
{trace_ts, Pid, call, MFA, Ts} ->
Stacks = State#state.stacks,
trace_listener(State#state{stacks=[MFA|Stacks]});
_Term ->
io:format("~p~n", [_Term]),
trace_listener(State)
end.
loop(0) -> ok;
loop(N) -> loop(N-1).
Whether there is return_to or not, it prints 101 {test,loop,1}.
I supposed that it should only return one {test,loop,1} with return_to.
No, the return_to flag doesn't disable the tail calls, it merely traces where/who the final result is returned to.
Change the code to fit to the screen:
...
loop(10),
timer:sleep(100),
Tracer ! dump.
With return_to enabled you will get the message:
{trace_ts,<0.348.0>,return_to,{test,test_tracer,0},{1405,153820,908198}}
It means the loop/1 function returned its result to test:test_tracer/0.
There's not such a message without return_to.
To get the return values you need to change the pattern:
erlang:trace_pattern({?MODULE, '_', '_'}, [{'_', [], [{return_trace}]}], [local]),
Now you will get the messages like:
{trace_ts,<0.657.0>,return_from,{test,loop,1},ok,{1405,155132,517284}}
(edit: i miss some reputation to create the bitcask tag so ...)
(tl;dr => bitcask:get/2 doesn't work and raises badarg in bitcask_nifs:keydir_get_int)
I would like to know how to use bitcask without riak the right way.
First, i was trying this:
bitcask:put(Handle, 3, {this, is, data}).
bitcask:get(Handle, 3).
This two calls raise the same error : badarg with erlang:size/1
The problem is erlang:size/1 accepts only binaries or tuples.
So i was trying this :
bitcask:put(Handle, {thing, 3}, {this, is, data}).
bitcask:get(Handle, {thing, 3}).
A new badarg error then, with erlang:crc32 and the Value i want to store.
So now i use this code, bucket is the atom name of a registered gen_server
which keeps the handle in its state. cask_wrapper is the code for theese
gen_servers. The code below is the acces to theese gen servers.
-module(sr_db).
...
get(Type, Key) when not is_binary(Key) ->
?MODULE:get(Type, term_to_binary(Key));
get(Type, Key) ->
Bucket = type2bucket(Type),
cask_wrapper:get(Bucket, {get, Key}).
put(Type, Key, Data) when not is_binary(Key) ->
?MODULE:put(Type, term_to_binary(Key), Data);
put(Type, Key, Data) when not is_binary(Data) ->
?MODULE:put(Type, Key, term_to_binary(Data));
put(Type, Key, Data) ->
Bucket = type2bucket(Type),
cask_wrapper:put(Bucket, Key, Data),
ok.
%% syncput(Type, Key, Data) -> call au lieu de cast
type2bucket(user) -> users_cask.
I use this code like this:
sr_db:get(user, 3).
%% then a call is made to cask_wrapper:get(users_cask, {get, 3}).
there are the cask_wrapper functions
get(Bucket, Key) ->
gen_server:call(Bucket, {get, Key}).
handle_call({get, Key}, _From, State) ->
Fetch = bitcask:get(State#state.handle, Key),
{reply, Fetch, State}.
I use the same mechanism with the put function. (but with gen_server:cast)
My first question is : is doing term_to_binary conversion in every call
a good practice, or is it slow ? I will have to convert back to erlang
terms the values that i fetch.
At the moment, the put operation returns 'ok'. It works. But the get
operation doesn't work yet. This is the error:
=ERROR REPORT==== 29-Jan-2012::20:21:24 ===
** Generic server users_cask terminating
** Last message in was {get,{get,<<131,97,3>>}}
** When Server state == {state,#Ref<0.0.0.353>}
** Reason for termination ==
** {badarg,[{bitcask_nifs,keydir_get_int,[<<>>,{get,<<131,97,3>>}]},
{bitcask_nifs,keydir_get,2},
{bitcask,get,3},
{cask_wrapper,handle_call,3},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
Bitcask dir : "/home/niahoo/src/skyraiders/priv/bitcasks/users"
options : [read_write]** exception exit: {{badarg,
[{bitcask_nifs,keydir_get_int,
[<<>>,{get,<<131,97,3>>}]},
{bitcask_nifs,keydir_get,2},
{bitcask,get,3},
{cask_wrapper,handle_call,3},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]},
{gen_server,call,[users_cask,{get,{get,<<131,97,3>>}}]}}
in function gen_server:call/2
I can't figure out why it does not work and would appreciate some help.
Thank you
Bitcask expects the key and the value both to be binaries (as you already noticed).
I don't really know how fast term_to_binary/binary_to_term is, but there is really no way around it if you want to store terms on disk. You could of course roll you own code to convert you keys and values to/from binaries, but I doubt that it will be significantly fast than the builtin functions and certainly less flexible.
But at the end of the day you have to measure the profile your application, and decide if term_to_binary/binary_to_term is a hotspot in your total system. I would be very surprised if that is the case in any real application where data has to be written to disk.
Now to the error when calling sr_db:get/2. You are wrapping the key twice inside a {get, Key} tuple, once inside sr_db:get/2 and another time in cask_wrapper:get/2, but you unwrap it only once, by matching in cask_wrapper:handle_call/3.
You can immediately spot this in the error report in those two lines:
** Last message in was {get,{get,<<131,97,3>>}}
and
{gen_server,call,[users_cask,{get,{get,<<131,97,3>>}}]}}