Distributed programming: Difference between revisions

Content added Content deleted
(Added Wren)
Line 1,915: Line 1,915:


Now you can enter commands in the client terminal and get the output back through the same connection.
Now you can enter commands in the client terminal and get the output back through the same connection.

=={{header|Wren}}==
{{trans|Go}}
{{libheader|WrenGo}}
{{libheader|Wren-fmt}}
As Wren has no networking support at present, we use embedded programs for both the server and client with a Go host using the net/rpc package in its standard library.

Moreover, as Wren's VM is not re-entrant, we need to run two VMs from the server side, one to call Go from Wren and the other to call Wren from Go.

'''Server:'''
<br>
We need two Wren scripts one for each VM:
<lang ecmascript>/* distributed_programming_server.wren */

class Rpc {
foreign static register()

foreign static handleHTTP()
}

foreign class Listener {
construct listen(network, address) {}
}

class HTTP {
foreign static serve(listener)
}

Rpc.register()
Rpc.handleHTTP()
var listener = Listener.listen("tcp", ":1234")
HTTP.serve(listener)</lang>
<br>
<lang ecmascript>/* distributed_programming_server2.wren */

class TaxComputer {
static tax(amount, rate) {
if (amount < 0) Fiber.abort("Negative values not allowed.")
return amount * rate
}
}</lang>
<br>
We now embed these scripts in the following Go program and run it on one terminal.
<lang go>/* go run distributed_programming_server.go */

package main

import(
wren "github.com/crazyinfin8/WrenGo"
"log"
"net"
"net/http"
"net/rpc"
)

type any = interface{}

type TaxComputer float64

var vm2 *wren.VM

var fileName = "distributed_programming_server.wren"
var fileName2 = "distributed_programming_server2.wren"

func (taxRate TaxComputer) Tax(x float64, r *float64) error {
wrenVar, _ := vm2.GetVariable(fileName2, "TaxComputer")
wrenClass, _ := wrenVar.(*wren.Handle)
defer wrenClass.Free()
wrenMethod, _ := wrenClass.Func("tax(_,_)")
defer wrenMethod.Free()
ret, _ := wrenMethod.Call(x, float64(taxRate))
*r = ret.(float64)
return nil
}

func register(vm *wren.VM, parameters []any) (any, error) {
c := TaxComputer(0.05) // 5% tax rate
rpc.Register(c)
return nil, nil
}

func handleHTTP(vm *wren.VM, parameters []any) (any, error) {
rpc.HandleHTTP()
return nil, nil
}

func serve(vm *wren.VM, parameters []any) (any, error) {
handle := parameters[1].(*wren.ForeignHandle)
ifc, _ := handle.Get()
listener := ifc.(*net.Listener)
http.Serve(*listener, nil)
return nil, nil
}

func listen(vm *wren.VM, parameters []any) (any, error) {
network := parameters[1].(string)
address := parameters[2].(string)
listener, err := net.Listen(network, address)
if err != nil {
log.Fatal(err)
}
return &listener, nil
}

func main() {
vm := wren.NewVM()
vm2 = wren.NewVM()
vm2.InterpretFile(fileName2)

rpcMethodMap := wren.MethodMap {
"static register()": register,
"static handleHTTP()": handleHTTP,
}

httpMethodMap := wren.MethodMap { "static serve(_)":serve }

classMap := wren.ClassMap {
"Listener": wren.NewClass(listen, nil, nil),
"Rpc" : wren.NewClass(nil, nil, rpcMethodMap),
"HTTP" : wren.NewClass(nil, nil, httpMethodMap),
}

module := wren.NewModule(classMap)
vm.SetModule(fileName, module)
vm.InterpretFile(fileName)
vm.Free()
vm2.Free()
}</lang>
<br>
'''Client:'''
<br>
Just one Wren script needed here:
<lang ecmascript>/* distributed_programming_client.wren */

import "./fmt" for Fmt

foreign class Client {
construct dialHTTP(network, address) {}

foreign call(serviceMethod, arg)
}

var client = Client.dialHTTP("tcp", "localhost:1234")
var amounts = [3, 5.6]
for (amount in amounts) {
var tax = client.call("TaxComputer.Tax", amount)
Fmt.print("Tax on $0.2f = $0.2f", amount, tax)
}</lang>
<br>
which we embed in the following Go program and run it on a different terminal.
<lang go>/* go run distributed_programming_client.go */

package main

import(
wren "github.com/crazyinfin8/WrenGo"
"log"
"net/rpc"
"strings"
)

type any = interface{}

func dialHTTP(vm *wren.VM, parameters []any) (any, error) {
network := parameters[1].(string)
address := parameters[2].(string)
client, err := rpc.DialHTTP(network, address)
if err != nil {
log.Fatal(err)
}
return &client, nil
}

func call(vm *wren.VM, parameters []any) (any, error) {
handle := parameters[0].(*wren.ForeignHandle)
ifc, _ := handle.Get()
client := ifc.(**rpc.Client)
serviceMethod := parameters[1].(string)
amount := parameters[2].(float64)
var tax float64
err := (*client).Call(serviceMethod, amount, &tax)
if err != nil {
log.Fatal(err)
}
return tax, nil
}

func moduleFn(vm *wren.VM, name string) (string, bool) {
if name != "meta" && name != "random" && !strings.HasSuffix(name, ".wren") {
name += ".wren"
}
return wren.DefaultModuleLoader(vm, name)
}

func main() {
cfg := wren.NewConfig()
cfg.LoadModuleFn = moduleFn
vm := cfg.NewVM()
fileName := "distributed_programming_client.wren"
clientMethodMap := wren.MethodMap { "call(_,_)": call }
classMap := wren.ClassMap { "Client": wren.NewClass(dialHTTP, nil, clientMethodMap) }
module := wren.NewModule(classMap)
vm.SetModule(fileName, module)
vm.InterpretFile(fileName)
vm.Free()
}</lang>

{{out}}
Output on the client terminal:
<pre>
Tax on 3.00 = 0.15
Tax on 5.60 = 0.28
</pre>


{{omit from|Lotus 123 Macro Scripting}}
{{omit from|Lotus 123 Macro Scripting}}