差值ok和end [Erlang]

dy1byipe  于 2022-12-08  发布在  Erlang
关注(0)|答案(1)|浏览(194)

在Erlang中,用end和ok结束函数有什么区别?我一直在下面的代码中试图理解它们的含义:

-module(esOne).
-export([start/1, func/1]).

start(Par) ->
    io:format("Client: I am ~p, spawned by the server: ~p~n",[self(),Par]),
    spawn(esOne, func, [self()]),
    Par ! {onPid, self()},
    serverEsOne ! {onName, self()},
    receiveMessage(),
    ok.

receiveMessage() ->
    receive
        {reply, N} ->
            io:format("Client: I received a message: ~p~n",[N])
    after
        5000->
            io:format("Client: I received no message, i quit~n",[])
    end.

func(Parent)->
    io:format("Child: I am ~p, spawned from ~p~n",[self(),Parent]).

这段代码与另一个作为服务器的.erl文件一起工作。我设法通过分析给定的服务器文件并复制它的行为来编写这段代码。首先我认为ok是用来结束每个函数的,但事实并非如此,因为我无法结束receiveMessage然后我想我可以用end来结束每个函数,但是start如果我用end替换ok,(Par)将给予一个错误。不仅如此,在服务器文件中,我看到ok和end在函数中用来结束循环。在我看来,它们的使用方式是一样的。然而,它们显然履行着一种单独的职能,因为一种职能不能被另一种职能所取代。

oipij1gg

oipij1gg1#

Two points to understand:

  • Some code block types in Erlang are closed with an "end". So if ... end , case ... end , receive ... [after N] ... end and so on. It is certainly possible to use "end" as its own atom in place of OK, but that is not what is happening above.
  • Every function in Erlang returns some value. If you aren't explicit about it, it returns the value of the last expression. The "=" operator isn't assignment to a variable like in other languages, it is assignment to a symbol as in math, meaning that reassigning is effectively a logical assertion. If the assertion fails, the process throws an exception (meaning it crashes, usually).

When you end something with "ok" or any other atom you are providing a known final value that will be returned. You don't have to do anything with it, but if you want the calling process to assert that the function completed or crash if anything unusual happened then you can:

do_stuff() ->
    ok = some_func().

instead of

do_stuff() ->
    some_func().

If some_func() may have had a side effect that can fail, it will usually return either ok or {error, Reason} (or something similar). By checking that the return was ok we prevent the calling process from continuing execution if something bad happened. That is central to the Erlang concept of "let it crash". The basic idea is that if you call a function that has a side-effect and it does anything unexpected at all, you should crash immediately, because proceeding with bad data is worse than not proceeding at all. The crash will be cleaned up by the supervisor, and the system will be restored to a known state instead of being in whatever random condition was left after the failure of the side-effect.
A variation on the bit above is to have the "ok" part appear in a tuple if the purpose of a function is to return a value. You can see this in any dict-type handling library, for example. The reason some data returning functions have a return type of {ok, Value} | {error, Reason} instead of just Value | {error, Reason} is to make pattern matching more natural.
Consider the following case clauses:

case dict:find(Key, Dict) of
    {ok, Value} ->
        Value;
    {error, Reason} ->
        log(error, Reason),
        error
end.

And:

case finder(Key, Struct) of
    Value ->
        Value;
    {error, Reason}
        log(error, Reason),
        error
end.

In the first example we match the success condition first. In the second version, though, this is impossible because the error clause could never match; any return at all would always be represented by Value . Oops.
Most of the time (but not quite always) functions that return a value or crash will return just the value. This is especially true of pure functions that carry no state but what you pass in and have no side effects (for example, dict:fetch/2 gives the value directly, or crashes the calling process, giving you an easy choice which way you want to do things). Functions that return a value or signal an error usually wrap a valid response in {ok, Value} so it is easy to match.

相关问题