guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Me no understand scoping


From: Maciek Godek
Subject: Re: Me no understand scoping
Date: Fri, 1 Aug 2008 01:07:17 +0200

>> Here comes another one:
>> Suppose I want to define a variable, but I don't know its name
>> -- it is contained in another variable. For example:
>> (define a 'b)
>> I want to assign a value to the symbol "contained" in a.
>> (that would be b in this example). The problem is that "define"
>> quotes its first argument. How to achieve it?
>
> I'm not so sure about this one, but I think that
>
> (eval `(define ,(car a) 1))
>
> should work.

I eventually came up with something similar:
(define (define@ name value) (primitive-eval `(define ,name ,value)))

> Now, stepping back a bit - and purely out of interest - did you
> mention before why you are doing these strange things?  Is it your way
> of exploring and learning, or is there (also) a specific requirement?

I can't tell you precisely (because I don't know myself).
It's just that whenever I try to do something in scheme,
I end up with questions of that sort. (Maybe the thing is
that I try to write as little code as possible -- the prettiness
and simplicity of scheme convinces me to do so).

I'm currently working on a little project (in my spare time).
I can't give you the source right now, because it's in its
infancy and there's definitely nothing to boast with.

However, I wrote the specification about a week ago
(originally it was meant to be a post to this group,
but as I wrote it, it replied to all my doubts and so
there was nothing to ask).

I hereby copy the contents of the specification file (to feed
your curiosity):

=BEGIN

Dear fellows,
For some time, I've been designing a network-oriented
object system, with interface callable from client side
and executed on server.

The toughest issue I have to deal with is the implementation
of RPC protocol.

The idea is fairly simple. Each client connected to
a server may request an object to be created, so the client
side session could look like this:

(noos-init)
(define connection (noos-connect address port))
; on success, noos-connect returns a valid connection handler
; or #f if fails
(if (not connection) (quit))

(noos-get-classes connection)
; noos-get-classes returns the list of valid noos-classes.
=> (class1 class2 class3)

; We can further inquire for the definition of a given class:
(noos-declaration connection 'class1)

; A noos-class declaration consists of:
; - list of public variables (readable by all clients connected)
; - list of private variables (readable by the owner of the class)
; - list of interface function prototypes. From the clients'
;   point of view, they consist of the name of the function and
;   the list of its parameters.
=> ((v1 v2 v3)(h1 h2 h3)((set-private v1 v2 v3)(do-something-else a1)))

; Knowing the interface, we can create instances of classes
(define y (noos-instance 'class1))
; noos-instance returns an object handler. it contains a local
; copy of hidden and public variables for immediate usage:
(y 'public)
=> #(0 0 0)
(y 'private)
=> #(0 0 0)
(y 'v1)
=> 0

; As you can see, the state is kept inside vectors, so they
; can be accessed very quickly.

; We can call the object's member functions:

(y 'set-private 1 2 3)
(y 'do-something-else "like what?")

; Note we can only pass numbers or strings as arguments,
; therefore the following calls would be invalid (regardless
; of the internal method definitions)

(!) (in y (set-public 'symbol1 (the-environment) #f))
(!) (in y (do-something-else (make-hash-table)))

; As the noos can be implemented over the UDP protocol,
; there's no guarrantee that the variables of out obiects
; are updated.

(y 'private)
=> (0 0 0)

; We can force it to do so using the `update'
; procedure if needed:

(update y)
(y 'private)
=> (1 2 3)

; It doesn't mean that the normal invocations of member
; functions are unable to change that state on client
; machine. If the packet is properly delivered to server,
; it immadietly changes the state of an object on the server.
; It also sends an event to a client -- it contains the new
; state of the object

; The update function sends request to a server and
; waits for the event to occur. If it doesn't (within
; specified amount of time), it sends it again and
; again, until the timeout is exceeded and then returns
; #f. If the response is sent properly, it returns #t
; (or something else different from #f)

; There's also another way of making sure that the
; remote member call is performed properly:

(assure (y 'set-public 1 2 3))

; This isn't currently implemented, but it looks tempting :D

         == SERVER SIDE ==

; OK, now that we know how the system looks like from client's
; point of view, it's time to see it from the server's perspective.

; First of all, there are class definitions

(noos-class class1
  (v1 v2 v3)
  (h1 h2 h3)
  (
    (set-private a1 a2 a3) (
      (set! v1 a1)
      (set! v2 a2)
      (set! v3 a3)
      (send-event owner state)
    )
    (do-something-else a1) (
      (send-event owner "do you really want to `" a1 "'")
    )
  )
  ; watch out! the next line defines an update function
  ; -- more about this later
  ((set! v1 (+ v1 h1)) (set! v2 (+ v2 h2)) (set! v3 (+ v3 h3)))
)

; As you can see, there are some variables of special
; meaning: namely 'owner' and 'state' (and a few others,
; like 'private', 'public' and 'neighbors' -- the last
; one to be explained later)

; noos adds a fairly sophisticated concept of meetings to
; the traditional object-oriented view on programming.
; In this concept, the world of objects is divided into
; subspaces (that are nothing but special objects that
; just hold references to their children)

; Obviously, there's a good reason for it. Under certain
; circumstances, two objects can meet. The circumstances
; are specified by a meeting predicate, ie.

(meeting-circumstances ((<sphere> x) (<sphere> y))
  (and
    (< (- (x 'v1) (y 'v1)) 1)
    (< (- (x 'v2) (y 'v2)) 1)
    (< (- (x 'v3) (y 'v3)) 1)
  )
)

       == IMPLEMENTATION ==

1. PROTOCOL

Firstly let's focus on how the underlaying protocol is
implemented. Information about the sender is contained
in the header of UDP packet -- therefore we don't have
to transmit any additional data.

; The server maintains a hash table in which all clients'
; addresses are stored.

(define clients-by-address (make-hash-table))

; When a client sends its first packet, a
; new entry in the hash table is created, assigning
; the client a unique identifier.

(define clients '())

(let ((cl (hash-ref clients-by-address address)))
  (if (not cl)
    (begin
      (set! clients (cons (new-client) clients))
      (hash-set! clients-by-address address (car clients))
    )

  (let ((object (vector-ref cl ob)))
    (let ((function (vector-ref (object 'interface)) fn))
      (local-call object function args)
    )
  )
)

; Is that all? Well, almost. The presented code assumes that
; when a packet is received, it is parsed to the variables
; ob, fn and args that are further processed by us.

; In practice that code is implemented in C (hoping it to
; perform better), as well as our parsing function.

; Current implementation uses the portable SDL_Net library
; to perform data exchange over UDP protocol.

; The protocol is text based and rather simple: client packets
; consist of an object number followed by a function number
; followed by argument list. The packets are therefore very
; lispish, although the types are limited to numbers, strings
; and lists.

; An example packet could look like this:
0 0 (1 (2 3))0+2i 3.4"one""two" (NULL-TERMINATED)

; Officially a single space is a separator, but the parser
; is written liberally and treats any sequence of white spaces
; as a separator. Like in lisp, no additional separator is needed
; after parentheses and double-quotes.

; Certainly this implementation of protocol isn't too efficient
; but an additional compression tunnel can be used in the future
; to provide faster data exchange (my goal now is just to make a
; working system, alright?)

// And this is how the packet is parsed (note we
// take advantage of the fact that the packets are
// meant to be null-terminated strings):

SCM parse(const char *packet) {
    static char *arglist[1024]; // or whatever the limitation of UDP is
    sprintf(arglist, "(quote (%s) )", packet);
    return eval(arglist);
}

// obviously, in this form the code is rather dangerous and
// it requires a more sophisticated control -- you have to be
// aware of this fact when using current implementation of
// noos. (don't say I didn't warn you). On the other hand,
// since the code here is quoted, it won't be executed unless
// you make it do so.

SCM parsed = parse(packet.data);
int ob = scm_to_int(car(parsed));
int fn = scm_to_int(cadr(parsed));
SCM args = cddr(parsed);

// And that's how it's done on server.
; Situation on client machine is to some extent similar.
; In this case, however, the object's method simply invokes
; a `remote-call' function with specified parameters.
; The remote call of a member function consists of:
; - getting an object id from object handler
; - getting a function id from object handler
; - passing these among with the rest arguments to a function
;   that builds a packet
; - sending the packet to the server.

; The other thing that can happen is the server signialising
; an event. This is also a remote-call, but invoked by the
; server and executed by the client.

=END

Surprisingly, this system isn't just a whim of a sick.
It's only a tiny part of a much larger whim: I'm making
a game engine. I already have a customizable input
system (it's the best: you can bind any thunk to any
key or any binary function to mouse move) and I'm
planning to make guile bindings to ogre3d display
engine (and several others) later on.

Obviously everybody's free to join :)
(but I warn you that I'm terribly fussy if it comes
to programming style (esp. my own), very likely
to change function and variable names and that
I don't have much experience in collective
programming)

I can let you know when I get the first working implementation
if you want.

Thanks for attention :)
M.

PS gdm rocks! (thankyouthankyouthankyou!! it really did change
my way of writing the code)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]