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

Comments on SRFI-39

Hello, guys.

I'm coming to this discussion somewhat late. Bravo, Marc, for tackling this one.

SRFI says:
    A parameter object created by the procedure make-mutable-parameter is
    a procedure which accepts zero or one argument (what happens
    when two or more arguments is given is completely
    implementation dependent). A parameter object created by the
    procedure make-parameter is a procedure which accepts no argument
    (what happens when one or more argument is given is completely
    implementation dependent). 

Why do we say extra args to a parm object is undefined? Why not error?

Why is PARAMETERIZE syntax? Make it a function, and render all issues
of eval-order irrelevant. Having captured the core idea as a procedure,
then define a litle piece of binding syntax in terms of it:
  (paramterize param val thunk)				; Procedure
  (let-parameter  ((param val) ...) body ...)		; Derived syntax
  (let-parameter* ((param val) ...) body ...)		; Derived syntax
Or punt the macros and just stick to basic functionality.

Why is the PROMPT mutable param implementation-dependent when passed "> "? It
seems like it's definitely defined, since it's a mutable parameter, to alter
the binding to "> ". Am I missing something here?

Matthew asked "Why mutable params?" I support them, with the *exact* semantics
Marc provides -- threads *inherit* the dynamic bindings. This allows thread
code to be written that interacts w/other threads via shared state accessed
from a parameter. The agent that forks off the threads controls the degree of
sharing or insulation by arranging sharing of parameter cells.

For example, consider a cursor-like object, such as a "current working
directory" or a pointer into a database table. Two threads might want to
move around in the directory structure or the table by altering the cursor.
One thread changes the cursor, the other sees it. You arrange this by
(1) binding the param to a fresh cell, then (2) spawning two threads in the
single dynamic scope of that binding.

Suppose, however, you want *independent* thread groups. No trouble there, 
either. You just spawn each thread or cooperating thread group in a
different dynamic-parameter binding.

So you have complete control of this kind of thing, and the degree of sharing
doesn't show up in the thread code itself. You can take the same code, and run
it in isolated solitude, in a group of two, a group of twenty, whatever you
like. In general, *any* kind of thread-global shared resource (like the
typical radix & standard-io examples) fits nicely into this scheme.

I know Mike S & Kelsey prefer to reset dynamic bindings to fresh
bindings with top-level values at thread-spawn boundaries. I think this
is completely broken. Spawning a thread, calling a function -- it's a
dynamic nesting. 

I readily admit you can implement mutable params with immutable params and a
one-elt vector. But I also buy Marc's story that this is not "the way of
Scheme" -- you could also "implement" mutable *variables* with immutable vars
& one-elt vectors, but Scheme doesn't. Also, if you emulate mutable params
with immutable params + mutable cells, you *expose the cell to parameter
clients* -- when you just want to expose the *value of the cell*. This can
lead to bugs, in the general way bugs happen when you expose the underlying
encoding of a thing (e.g., you encode a structure as a three-element list or
something like that). Providing mutable params as a primitive locks up the
internal cell so buggy clients can't accidentally fetch it out, store it away,
pass it around, reference it outside of scope, etc.

Sound like an abstract objection? It's pretty concrete, actually, by which I
mean that it's not an unrealistic bug -- it's the kind of bug you would expect
to see in real code. The bug will be someone fetching the cell from the param
and forgetting to dereference it, so the mutable cell, intended to be internal
to the param, will propagate through the program the way the cell's value was
intended to propagate.

I do not like the whole idea of packaging up this functionality as
procedures; I'd rather they be a new data type. But Marc has motivated
his decision to do this -- staying close to existing APIs while focussing
on the tricky semantic decisions. I can roll with that, and won't even
complain about how much I hate the ambiguous, confusing term "parameter."