4 ms·
Unfortunately, Erlang does not directly implement Actor behavior change. Consequently, Erlang cannot directly implement Actor behavior change. A factorial ser
by ProfHewitt 7y ago
Unfortunately, Erlang does not directly implement Actor behavior change.
Consequently, Erlang cannot directly implement Actor behavior change. A factorial server is implemented below:
FactorialServer[ ] *implements* ProcedureServer<[NaturalNumber], NaturalNumber>
[i] |-> // received message [i]
Factorial.[i] // return result of sending [i] to Factorial
In order to implement Actor behavior change, Erlang must resort to using helper processes that trampoline back so that helper processes can return the desired change.
See the following for direct implementation of Actor behavior change:
https://papers.ssrn.com/abstract=3418003
PS. The type ProcedureServer above can be defined as follows:
ProcedureServer<t1, t2> ≡ // Procedure server with parameters types t1 and t2 is defined to be
t1 -> t2 // type of procedure from t1 into t2
- gmfawcett 7y agoBy "Actor" I see that you're referring to a particular theory named "Actor", you're not using the term in a colloquial sense. You seem to be saying that Erlang can implement "Actor behaviour change", just not directly. In what sense is this a concern? i.e., what value does direct implementation add here?
- ProfHewitt 7y agoYes, Actor is used in the technical sense axiomatized up to a unique isomorphism in the linked article and not in the common usage of a thespian ;-) Erlang not being able to directly express Actor behavior change means that Erlang programs are more obscure and convoluted. For example, consider the following program: ProcessorController[pk:PublicKey] *implements* controller // initialize processor controller with a public key pk currrentVersion ≔ 1 // processor current version number is initially 1 running ← Crowd [1] // running is a crowd of at most 1 running activities boot[*itemIs* i *whichIs* Package[*imageIS* code, *versionIs* packageVersion], signedIs s] ↦ // received boot request with item i and signed s *IsEmpty* running *cases* // Check if the processor is running True ⇾ // If not running, then SigningChecker.[*itemIs* i, *signedIs* s, *publicKeyIs* pk] *cases* // check that i was signed by s with private key for pk True ⇾ // If signing check succeeds, then currentVersion⩽packageVersion *cases* // check processor current version is // less or equal than package version True ⇾ // If version check succeeds, then (currentVersion ≔ packageVersion; // first update current version number, code.run thru running) // afterward run code in a hole of mutual exclusion passing // thru running then returning a termination report False ⇾ // If version check fails, *Throw* BadVersionException[] // throw bad version exception False ⇾ *Throw* BadSignatureException[] // If signature check fails, False ⇾ // If already running, then *Throw* AlreadyRunningException[] // throw already running exception shutDown ↦ // received shut down request *IsEmpty* running *cases* // Check if the processor is running True ⇾ // If not running, then *Throw* NotRunningException[] //throw not running exception False ⇾ // If already running, then *Cancel* running // cancel running returning Void The challenge in Erlang is to implement the hole in the region of mutual exclusion in the above implementation.
- gmfawcett 7y agoHah, of course I meant the "techie" colloquial of Actor, not the thepsian one. But I suppose that my comment is true in both senses. :) Thanks for clarifying. I'm not familiar with your theory, but at least I understand why you raised the point -- some Erlang programs are needlessly complicated due to the language's (colloquial!) actor semantics.
- ProfHewitt 7y agoYou are very welcome gmfawcett! What Erlang programs have you found to be needlessly complicated?
- dnautics 7y ago> Some Erlang programs are needlessly complicated due to the language's (colloquial!) actor semantics. Maybe you're referring to junior-to-BEAM programmers reaching to prove they can use gen_servers everywhere? There are also obscure corners of OTP that are a bit of a mess, but I'd say that's because it's inherited a lot of C-style folkways and doesn't have hierarchical module aliasing in the same way that say Elixir has. And there's a substantial amount of using modules-like-factories that got picked up in certain corners of the ecosystem, probably from Java habits, but I don't see that as much in Elixir, which suggests that it's not the actor semantics so much as, just outdated programming trends that have accumulated in the erlang ecosystem by virtue of it being older, and a "less popular" language that other people try to shove their round pegs into (arguably elixir is the latest iteration of that but they seem to have made some really good choices, at least in my opinion).
- toast0 7y agoHow about something like this? I don't understand the code you wrote exactly, and I smooshed the processor into the controller, because it simplifies things. fun Self (PublicKey) -> CurrentVersion = 1, receive {upgrade, Function, Version, Signature} -> case sign_checker(PublicKey, Function, Version, Signature) of true when Version > CurrentVersion -> Function(PublicKey); true -> throw(bad_version); false -> throw(bad_signature) end; shutdown -> ok; _Other -> % here's where you handle work % But I don't know what work you wanted to do, % so I'm just looping Self(PublicKey) end end. You probably don't actually want to throw for these errors (why should the server stop if it got an invalid request?), and you probably want your messages to include a reply tag, so you can get the results (like gen_server:gen_call/2,3 does), but you know, examples on the interwebs; don't run them as-is.