Related
I have recently started learning Erlang and I am trying to implement a server-client sample program. I have created a registered process and I would like to send data to it from another process. The code is as follows.
-module(mine).
-export([alice/0, bob/2, startAlice/0, startBob/1]).
alice() ->
receive
{message, BobNode} ->
io:fwrite("Alice got a message \n"),
BobNode ! message,
alice()
finished -> io:fwrite("Alice is finished\n")
end.
bob(0, AliceNode) ->
{alice, AliceNode} ! finished,
io:fwrite("Bob is finished\n");
bob(N, AliceNode) ->
{alice, AliceNode} ! {message, self()},
receive
message -> io:fwrite("Bob got a message ~w \n",[N])
end,
bob(N-1, AliceNode).
startAlice() ->
register(alice, spawn(mine, alice, [])).
startBob(AliceNode) ->
spawn(mine, bob, [30000, AliceNode]).
Here, I would like to send some value say N, from bob to alice. I tried sending the data as
{alice, AliceNode, Nvalue} ! {message, self(), N} in bob(N, AliceNode) function, but got the error variable 'Nvalue' is unbound erl. I am sure I am missing something trivial here. Any help would be appreciated. Thanks in advance.
Consider the following minimal [?] example:
defmodule Foo do
def bar() do
n = IO.read(:line) |> String.trim() |> String.to_integer()
for _ <- 0..n - 1 do
IO.read(:line) |> IO.write()
end
end
end
import ExUnit.CaptureIO
capture_io("2\nabc\ndef", Foo.bar)
I did look into the documentation, and it puts no limitations on ExUnit.CaptureIO usage, but the aforelisted code hangs, waiting for the first line of input, as if it hasn't been fed. Have I missed something?
If it matters, I'm using Elixir 1.7.3.
The second argument to capture_io needs to be a function to run with the capturing enabled. Here, you're passing in the result of running Foo.bar. That hangs forever, as it is expecting input from stdio, which never comes. Long story short you need to pass it as a function:
capture_io("2\nabc\ndef", &Foo.bar/0)
because Foo.bar is the same as Foo.bar().
I'm writing a web game in Elm with lot of time-dependent events and I'm looking for a way to schedule an event at a specific time delay.
In JavaScript I used setTimeout(f, timeout), which obviously worked very well, but - for various reasons - I want to avoid JavaScript code and use Elm alone.
I'm aware that I can subscribe to Tick at specific interval and recieve clock ticks, but this is not what I want - my delays have no reasonable common denominator (for example, two of the delays are 30ms and 500ms), and I want to avoid having to handle a lot of unnecessary ticks.
I also came across Task and Process - it seems that by using them I am somehow able to what I want with Task.perform failHandler successHandler (Process.sleep Time.second).
This works, but is not very intuitive - my handlers simply ignore all possible input and send same message. Moreover, I do not expect the timeout to ever fail, so creating the failure handler feels like feeding the library, which is not what I'd expect from such an elegant language.
Is there something like Task.delayMessage time message which would do exactly what I need to (send me a copy of its message argument after specified time), or do I have to make my own wrapper for it?
An updated and simplified version of #wintvelt's answer for Elm v0.18 is:
delay : Time.Time -> msg -> Cmd msg
delay time msg =
Process.sleep time
|> Task.perform (\_ -> msg)
with the same usage
One thing that may not be obvious at first is the fact that subscriptions can change based on the model. They are effectively evaluated after every update. You can use this fact, coupled with some fields in your model, to control what subscriptions are active at any time.
Here is an example that allows for a variable cursor blink interval:
subscriptions : Model -> Sub Msg
subscriptions model =
if model.showCursor
then Time.every model.cursorBlinkInterval (always ToggleCursor)
else Sub.none
If I understand your concerns, this should overcome the potential for handling unnecessary ticks. You can have multiple subscriptions of different intervals by using Sub.batch.
If you want something to happen "every x seconds", then a subscription like solution, as described by #ChadGilbert is what you need. (which is more or less like javascript's setInterval().
If, on the other hand you want something to happen only "once, after x seconds", then Process.sleep route is the way to go. This is the equivalent of javascript's setTimeOut(): after some time has passed, it does something once.
You probably have to make your own wrapper for it. Something like
-- for Elm 0.18
delay : Time -> msg -> Cmd msg
delay time msg =
Process.sleep time
|> Task.andThen (always <| Task.succeed msg)
|> Task.perform identity
To use e.g. like this:
---
update msg model =
case msg of
NewStuff somethingNew ->
...
Defer somethingNew ->
model
! [ delay (Time.second * 5) <| NewStuff somethingNew ]
Elm v0.19
To execute once and delay:
delay : Float -> msg -> Cmd msg
delay time msg =
-- create a task that sleeps for `time`
Process.sleep time
|> -- once the sleep is over, ignore its output (using `always`)
-- and then we create a new task that simply returns a success, and the msg
Task.andThen (always <| Task.succeed msg)
|> -- finally, we ask Elm to perform the Task, which
-- takes the result of the above task and
-- returns it to our update function
Task.perform identity
To execute a repeating task:
every : Float -> (Posix -> msg) -> Sub msg
I'm connecting to an DB source with Erlang ODBC. My code looks like:
main() ->
Sql = "SELECT 1",
Connection = connect(),
case odbc:sql_query(Connection, Sql) of
{selected, Columns, Results} ->
io:format("Success!~n Columns: ~p~n Results: ~p~n",
[Columns, Results]),
ok;
{error, Reason} ->
{error, Reason}
end.
connect() ->
ConnectionString = "DSN=dsn_name;UID=uid;PWD=pqd",
odbc:start(),
{ok, Conn} = odbc:connect(ConnectionString, []),
Conn.
It's ok now. But how can I handle errors at least in my query? As I understand it contains in {error, Reason}, but how can I output it when something gone wrong? I'm trying to add io:format like at the first clause, but it doesn't work.
At second, unfortunately, I can't find any reference that can explain syntax well, I can't understand what does ok mean in this code (first - line 8, and second - line 16. If I'm right it just means the case that connection is ok and this variable isn't assigned? But what it means at 8 line?)
ok in line 8 is the return value of the case statement when the call to odbc:sql_query(Connection, Sql) returns a result that can match the expression {selected, Columns, Results}. In this case it is useless since the function io:format(...) already returns ok.
the second ok: {ok, Conn} is a very common Erlang usage: the function returns a tuple {ok,Value} in case of success and {error,Reason} in case of failure. So you can match on the success case and extract the returned value with this single line: {ok, Conn} = odbc:connect(ConnectionString, []),
In this case the function connect() doesn't handle the error case, so this code has 4 different possible behaviors:
It can fails to connect to the database: the process will crash with a badmatch error at line 16.
It connects to the database but the query fails: the main function will return the value {error,Reason}.
It connects to the database and the query returns an answer that doesn't match the tuple {selected, Columns, Results}: the process will crash with a badmatch error at line 4.
It connects to the database and the query returns an answer that matches the tuple {selected, Columns, Results}: the function will print
Success!
Columns: Column
Results: Result
and returns ok
So I found something. The {error, Reason} contains the connection errors, means that we specified wrong DSN name etc. Regarding to my offer to catch query error we can read this from Erlang reference:
Gaurds All API-functions are guarded and if you pass an argument of
the wrong type a runtime error will occur. All input parameters to
internal functions are trusted to be correct. It is a good programming
practise to only distrust input from truly external sources. You are
not supposed to catch these errors, it will only make the code very
messy and much more complex, which introduces more bugs and in the
worst case also covers up the actual faults. Put your effort on
testing instead, you should trust your own input.
Means that we should be careful about what we write. That's not bad.
I'm basically following the tutorial on this site Learn you some Erlang:Designing a concurrent application and I tried to run the code below with the following commands and got an error on line 48. I did turn off my firewall just in case that was the problem but no luck. I'm on windows xp SP3.
9> c(event).
{ok,event}
10> f().
ok
11> event:start("Event",0).
=ERROR REPORT==== 9-Feb-2013::15:05:07 ===
Error in process <0.61.0> with exit value: {function_clause,[{event,time_to_go,[0],[{file,"event.erl"},{line,48}]},{event,init,3,[{file,"event.erl"},{line,31}]}]}
<0.61.0>
12>
-module(event).
-export([start/2, start_link/2, cancel/1]).
-export([init/3, loop/1]).
-record(state, {server,
name="",
to_go=0}).
%%% Public interface
start(EventName, DateTime) ->
spawn(?MODULE, init, [self(), EventName, DateTime]).
start_link(EventName, DateTime) ->
spawn_link(?MODULE, init, [self(), EventName, DateTime]).
cancel(Pid) ->
%% Monitor in case the process is already dead
Ref = erlang:monitor(process, Pid),
Pid ! {self(), Ref, cancel},
receive
{Ref, ok} ->
erlang:demonitor(Ref, [flush]),
ok;
{'DOWN', Ref, process, Pid, _Reason} ->
ok
end.
%%% Event's innards
init(Server, EventName, DateTime) ->
loop(#state{server=Server,
name=EventName,
to_go=time_to_go(DateTime)}).
%% Loop uses a list for times in order to go around the ~49 days limit
%% on timeouts.
loop(S = #state{server=Server, to_go=[T|Next]}) ->
receive
{Server, Ref, cancel} ->
Server ! {Ref, ok}
after T*1000 ->
if Next =:= [] ->
Server ! {done, S#state.name};
Next =/= [] ->
loop(S#state{to_go=Next})
end
end.
%%% private functions
time_to_go(TimeOut={{_,_,_}, {_,_,_}}) ->
Now = calendar:local_time(),
ToGo = calendar:datetime_to_gregorian_seconds(TimeOut) -
calendar:datetime_to_gregorian_seconds(Now),
Secs = if ToGo > 0 -> ToGo;
ToGo =< 0 -> 0
end,
normalize(Secs).
%% Because Erlang is limited to about 49 days (49*24*60*60*1000) in
%% milliseconds, the following function is used
normalize(N) ->
Limit = 49*24*60*60,
[N rem Limit | lists:duplicate(N div Limit, Limit)].
It's running purely locally on your machine so the firewall will not affect it.
The problem is the second argument you gave when you started it event:start("Event",0).
The error reason:
{function_clause,[{event,time_to_go,[0],[{file,"event.erl"},{line,48}]},{event,init,3,[{file,"event.erl"},{line,31}]}]}
says that it is a function_clause error which means that there was no clause in the function definition which matched the arguments. It also tells you that it was the function event:time_to_go/1 on line 48 which failed and that it was called with the argument 0.
It you look at the function time_to_go/ you will see that it expects its argument to be a tuple of 2 elements where each element is a tuple of 3 elements:
time_to_go(TimeOut={{_,_,_}, {_,_,_}}) ->
The structure of this argument is {{Year,Month,Day},{Hour,Minute,Second}}. If you follow this argument backwards you that time_to_go/ is called from init/3 where the argument to time_to_go/1, DateTime, is the 3rd argument to init/3. Almost there now. Now init/3 is the function which the process spawned in start/2 (and start_link/2) and the 3rd argument toinit/3is the second argument tostart/2`.
So when you call event:start("Event",0). it is the 0 here which is passed into the call time_to_go/1 function in the new peocess. And the format is wrong. You should be calling it with something like event:start("Event", {{2013,3,24},{17,53,62}}).
To add background to rvirding's answer, you get the error because the
example works up until the final code snippet as far
as I know. The normalize function is used first, which deals with the
problem. Then the paragraph right after the example in the question
above, the text says:
And it works! The last thing annoying with the event module is that we
have to input the time left in seconds. It would be much better if we
could use a standard format such as Erlang's datetime ({{Year, Month,
Day}, {Hour, Minute, Second}}). Just add the following function that
will calculate the difference between the current time on your
computer and the delay you inserted:
The next snippet introduces the code bit that takes only a date/time and
changes it to the final time left.
I could not easily link to all transitional versions of the file, which
is why trying the linked file directly with the example doesn't work
super easily in this case. If the code is followed step by step, snippet
by snippet, everything should work fine. Sorry for the confusion.