int const *** const ** const * x; /* legal C :) */
I expect some unintuitive behavior, if the frozen references
are first class and can be stored in another objects and mixed
with mutable references. For example, what can you tell about
these forms:
($let ((x (freeze y))) (append! x z))
($when (not? (frozen? x)) (append! x y))
Do they signal an error? When?
> applicatives' names are from Ruby.
[...]
> *Summary of additions:*
>
> (freeze . objects)
>
> Applicative that returns references to the objects passed to it that
> have been marked as frozen.
>
Nitpicking: Should freeze really allow more than one argument?
Kernel Language does not have multiple value return facility
like CL or Scheme. Multiple results are usually (AFAIK) returned
in lists, e.g. get-list-metrics. This convention does not seem
fit freeze well. If (freeze x y) returns a two element list
(freeze x y) == (list (freeze x) (freeze y))
then (freeze x) should also return a list. But it does not,
so better restrict freeze to one argument (like e.g. symbol->string).
> (frozen? . objects)
>
> Applicative that returns true iff all references in objects have been
> marked as frozen.
>
Does it mean that (frozen? x) scans recursively all references contained
in x? If so, how it does handle encapsulated types?
($define! x (list (list 1 2) 3))
($define! y (freeze x))
(frozen? y) ; #t or #f ?
(frozen? (car y)) ; #t or #f ?
[...]
> Also, as freeze does not modify an object the passing frozen and
> unfrozen references to equ? and equal? should result in #t as they are
> still the same object.
>
[...]
>
> ($let ((x (get-current-environment)) (y (freeze (get-current-environment))))
> ($if ($and (equ? x y) ($not (frozen? x))) ; evaluates true*, they
> refer to the same environment even though y is frozen.
> ($set! x n 42) ; no error, x is not frozen* even if y is
> ($set! y n 21) ; error, y is marked as frozen
> )
> )
>
If equ? means eq?, I tend to disagree. It is a matter of opinion,
but I would like (eq? x y) imply that x and y are indistinguishable,
even though the Kernel Report section 3.7 suggests that it may
not be the case. Your example shows that the program can tell
the frozen reference from the original one.
[...]
Best wishes,
Oto Havle.
If the proposal seems like a reasonable addition to klisp, I will work on implementing the needed changes as life permits once pull request for some MinGW specific fixes I submitted has been accepted or declined.
I am waiting on that since I plan to delete my fork and refork before doing more work because being unfamiliar with Mercurial I made a bit of a mess in the repository (the branch for the pull request is not affected).
--
You received this message because you are subscribed to the Google Groups "klisp" group.
To unsubscribe from this group and stop receiving emails from it, send an email to klisp+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
> *Summary of additions:*
>
> (freeze . objects)
>
> Applicative that returns references to the objects passed to it that
> have been marked as frozen.
>
Nitpicking: Should freeze really allow more than one argument?
Kernel Language does not have multiple value return facility
like CL or Scheme. Multiple results are usually (AFAIK) returned
in lists, e.g. get-list-metrics. This convention does not seem
fit freeze well. If (freeze x y) returns a two element list
(freeze x y) == (list (freeze x) (freeze y))
then (freeze x) should also return a list. But it does not,
so better restrict freeze to one argument (like e.g. symbol->string).
Yes, it should operate on only one object to be more consistent with the rest of the language.
(freeze x) instead of (freeze . objects)
I was thinking of how $define! and $set! work at the time I was writing it up.
($define (x y z) (freeze a b c))
> (frozen? . objects)
>
> Applicative that returns true iff all references in objects have been
> marked as frozen.
>
Does it mean that (frozen? x) scans recursively all references contained
in x? If so, how it does handle encapsulated types?
($define! x (list (list 1 2) 3))
($define! y (freeze x))
(frozen? y) ; #t or #f ?
(frozen? (car y)) ; #t or #f ?
I do not think it should. If you are passing a list around enough that you need freeze, you probably should be using an encapsulation representing a list instead.
[...]
> Also, as freeze does not modify an object the passing frozen and
> unfrozen references to equ? and equal? should result in #t as they are
> still the same object.
>
[...]
>
> ($let ((x (get-current-environment)) (y (freeze (get-current-environment))))
> ($if ($and (equ? x y) ($not (frozen? x))) ; evaluates true*, they
> refer to the same environment even though y is frozen.
> ($set! x n 42) ; no error, x is not frozen* even if y is
> ($set! y n 21) ; error, y is marked as frozen
> )
> )
>
If equ? means eq?, I tend to disagree. It is a matter of opinion,
but I would like (eq? x y) imply that x and y are indistinguishable,
even though the Kernel Report section 3.7 suggests that it may
not be the case. Your example shows that the program can tell
the frozen reference from the original one.
[...]
Best wishes,
Oto Havle.
To me the obvious implementation of eq? is to check if the references point to the same memory.
Regarding the proposal:While trying the control mutablity is a worthy goal, I'm not quite sure this is the way to go.
Please, don't read any of the following as a discouragement. Just a little food for thought, based
on my personal opinion.I favor the use of new encapsulated types with optional immutablity specified in the constructor because
they are easier to reason about and don't require modifcation to the underlying system.
Disclaimer: I'm not particularly fond of the autobox/autounbox feature personally, but some people are.with either a one element array or using a cons cell and encapsulation. You can allow both mutable andFor example see boxes in the racket docs: http://docs.racket-lang.org/reference/boxes.html).In this context, a box is the minimal element mutable storage. They can be easily implemented in klisp
immutable boxes, with the accessor & type-predicate common to both kinds.Now a way to easily create this type of encapsulations would be nice (say a constructor that returns five
procedures: a constructor for immutables, a constructor for mutables, a type-predicate, a mutable-predicate
and an accessor. This is more or less trivial to write in terms of make-encapsulation and enables easy
construction of types like the "box" described above (An alternative to this would be a general
record-definition for new types)Of course there are some drawbacks to this approach, some of which you indentified in your proposal.
and some of them have workarounds.For a proposed method of making box use more transparent see the srfi 111:
http://srfi.schemers.org/srfi-111/srfi-111.html
As for the use case of giving access to internal data, I'm not sure it justifies the introduction of references
as first class objects (and remember every entity in Kernel should be first class). I feel there's always a
cleaner way of giving access to internal data: via procedures, boxes, copies, iterators, cursors, etc.
Once you add (possibly mutable) references as first class object you have to allow to distinguish between
them and the object they refer to (otherwise they wouldn't be first class). And once you do that: what's the
difference with boxes? Furthermore, for some special use cases you may need to modify the basic workingsof the interpreter (for auto-boxing/auto-unboxing) and after all that trouble, what have you gained really?
I think references work well in environments where they are second class & the compiler
understands all uses of them and can reason about them statically. C++ is a good example of that.Regards,Andrés Navarro