Login  Register

Proposal for LiteralArray class

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options Options
Embed post
Permalink
Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Proposal for LiteralArray class

Ben Coman
5447 posts

Looking into Image locking problems [1] caused by a recursive array such as this...

    literalArray := #( 1 2 3 ).
    literalArray at: 3 put: literalArray.

I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...  

  Object>>isLiteral   ^false
  Boolean>>isLiteral ^true
  Character>>isLiteral ^true
  Integer>>isLiteral ^true
  String>>isLiteral ^true
  UndefinedObject>>isLiteral ^true

  ByteArray>>isLiteral ^self class == ByteArray
  Float>>isLiteral ^self isFinite "^(self - self) = 0.0"
  ScaledDecimal>>isLiteral ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]

  Array>>isLiteral ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]

...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).  

    Array>>printOn: aStream
self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
super printOn: aStream

Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.

Also, instead of a recursive Array>>isLiteral we'd have something like
  LiteralArray>>isLiteral ^true
  Array>>isLiteral ^false
which seems to align much better with the pattern of the other #isLiteral implementors.

I notice there is both RBArrayNode and RBLiteralArrayNode.

So what are the wider concerns that might apply?
(In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)

cheers -ben

Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Nicolai Hess
1347 posts


2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:

Looking into Image locking problems [1] caused by a recursive array such as this...

    literalArray := #( 1 2 3 ).
    literalArray at: 3 put: literalArray.

I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...  



Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.

shouldBePrintedAsLiteralVisiting: aSet
    self class == Array ifFalse:
        [^false].
    (aSet includes: self) ifTrue:
        [^false].
    aSet add: self.
    ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]


isn't there a common pattern to handle this kind of potential endless recursion?



 
  Object>>isLiteral   ^false
  Boolean>>isLiteral ^true
  Character>>isLiteral ^true
  Integer>>isLiteral ^true
  String>>isLiteral ^true
  UndefinedObject>>isLiteral ^true

  ByteArray>>isLiteral ^self class == ByteArray
  Float>>isLiteral ^self isFinite "^(self - self) = 0.0"
  ScaledDecimal>>isLiteral ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]

  Array>>isLiteral ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]

...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).  

    Array>>printOn: aStream
self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
super printOn: aStream

Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.

Also, instead of a recursive Array>>isLiteral we'd have something like
  LiteralArray>>isLiteral ^true
  Array>>isLiteral ^false
which seems to align much better with the pattern of the other #isLiteral implementors.

I notice there is both RBArrayNode and RBLiteralArrayNode.

So what are the wider concerns that might apply?
(In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)

cheers -ben


Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Ben Coman
5447 posts


On Sun, Feb 1, 2015 at 7:39 PM, Nicolai Hess <[hidden email]> wrote:


2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:

Looking into Image locking problems [1] caused by a recursive array such as this...

    literalArray := #( 1 2 3 ).
    literalArray at: 3 put: literalArray.

I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...  



Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.

shouldBePrintedAsLiteralVisiting: aSet
    self class == Array ifFalse:
        [^false].
    (aSet includes: self) ifTrue:
        [^false].
    aSet add: self.
    ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]


isn't there a common pattern to handle this kind of potential endless recursion?


There was a squeak discussion including a passing suggestion for a generic DepthFirstfTraversal safe against cyclic structures...
 


  Object>>isLiteral   ^false
  Boolean>>isLiteral ^true
  Character>>isLiteral ^true
  Integer>>isLiteral ^true
  String>>isLiteral ^true
  UndefinedObject>>isLiteral ^true

  ByteArray>>isLiteral ^self class == ByteArray
  Float>>isLiteral ^self isFinite "^(self - self) = 0.0"
  ScaledDecimal>>isLiteral ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]

  Array>>isLiteral ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]

...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).  

    Array>>printOn: aStream
self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
super printOn: aStream

Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.

Also, instead of a recursive Array>>isLiteral we'd have something like
  LiteralArray>>isLiteral ^true
  Array>>isLiteral ^false
which seems to align much better with the pattern of the other #isLiteral implementors.

I notice there is both RBArrayNode and RBLiteralArrayNode.

So what are the wider concerns that might apply?
(In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)

cheers -ben





@Stef/Marcus,  As part of this, since Array>>#shouldBePrintedAsLiteral implementation is the same as the Array>>isLiteral I was going to suggest removing the former, but I see the introduction of #shouldBePrintedAsLiteral was popular with you guys [1].   Is there some reason why Array>>shouldBePrintedAsLiteral should not be "^self isLiteral" or just removed, so Object>>shouldBePrintedAsLiteral is inherited ?


cheers -ben
Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Eliot Miranda-2
9490 posts
In reply to this post by Nicolai Hess


On Sun, Feb 1, 2015 at 3:39 AM, Nicolai Hess <[hidden email]> wrote:


2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:

Looking into Image locking problems [1] caused by a recursive array such as this...

    literalArray := #( 1 2 3 ).
    literalArray at: 3 put: literalArray.

I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...  



Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.

shouldBePrintedAsLiteralVisiting: aSet
    self class == Array ifFalse:
        [^false].
    (aSet includes: self) ifTrue:
        [^false].
    aSet add: self.
    ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]


isn't there a common pattern to handle this kind of potential endless recursion?

At Cadence we fixed it thus:

Object>>shouldBePrintedAsLiteral

^self isLiteral

Array>>shouldBePrintedAsLiteral

^self class == Array
  and: [self shouldBePrintedAsLiteralVisiting: (IdentitySet new: 8)]

Object>>shouldBePrintedAsLiteralVisiting: aSet

^self isLiteral

 Array>>shouldBePrintedAsLiteralVisiting: aSet
self class == Array ifFalse:
[^false].
(aSet includes: self) ifTrue:
[^false].
aSet add: self.
^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]

  Object>>isLiteral   ^false
  Boolean>>isLiteral ^true
  Character>>isLiteral ^true
  Integer>>isLiteral ^true
  String>>isLiteral ^true
  UndefinedObject>>isLiteral ^true

  ByteArray>>isLiteral ^self class == ByteArray
  Float>>isLiteral ^self isFinite "^(self - self) = 0.0"
  ScaledDecimal>>isLiteral ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]

  Array>>isLiteral ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]

...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).  

    Array>>printOn: aStream
self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
super printOn: aStream

Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.

Also, instead of a recursive Array>>isLiteral we'd have something like
  LiteralArray>>isLiteral ^true
  Array>>isLiteral ^false
which seems to align much better with the pattern of the other #isLiteral implementors.

I notice there is both RBArrayNode and RBLiteralArrayNode.

So what are the wider concerns that might apply?
(In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)

cheers -ben





--
best,
Eliot
Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Nicolai Hess
1347 posts
2015-02-02 3:03 GMT+01:00 Eliot Miranda <[hidden email]>:


On Sun, Feb 1, 2015 at 3:39 AM, Nicolai Hess <[hidden email]> wrote:


2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:

Looking into Image locking problems [1] caused by a recursive array such as this...

    literalArray := #( 1 2 3 ).
    literalArray at: 3 put: literalArray.

I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...  



Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.

shouldBePrintedAsLiteralVisiting: aSet
    self class == Array ifFalse:
        [^false].
    (aSet includes: self) ifTrue:
        [^false].
    aSet add: self.
    ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]


isn't there a common pattern to handle this kind of potential endless recursion?

At Cadence we fixed it thus:

Object>>shouldBePrintedAsLiteral

^self isLiteral

Array>>shouldBePrintedAsLiteral

^self class == Array
  and: [self shouldBePrintedAsLiteralVisiting: (IdentitySet new: 8)]

Object>>shouldBePrintedAsLiteralVisiting: aSet

^self isLiteral

 Array>>shouldBePrintedAsLiteralVisiting: aSet
self class == Array ifFalse:
[^false].
(aSet includes: self) ifTrue:
[^false].
aSet add: self.
^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]


Is there something more "generic". Something we can use for any object tracing.
Isn't there something the GC uses? The GC obviously does not fall into this loop.
(It flags visited objects, but there is nothing exposed that can be used
at the image level?)
How do ImageSegment or Fuel work with recursive structures?



Nicolai

 

  Object>>isLiteral   ^false
  Boolean>>isLiteral ^true
  Character>>isLiteral ^true
  Integer>>isLiteral ^true
  String>>isLiteral ^true
  UndefinedObject>>isLiteral ^true

  ByteArray>>isLiteral ^self class == ByteArray
  Float>>isLiteral ^self isFinite "^(self - self) = 0.0"
  ScaledDecimal>>isLiteral ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]

  Array>>isLiteral ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]

...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).  

    Array>>printOn: aStream
self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
super printOn: aStream

Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.

Also, instead of a recursive Array>>isLiteral we'd have something like
  LiteralArray>>isLiteral ^true
  Array>>isLiteral ^false
which seems to align much better with the pattern of the other #isLiteral implementors.

I notice there is both RBArrayNode and RBLiteralArrayNode.

So what are the wider concerns that might apply?
(In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)

cheers -ben





--
best,
Eliot

Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Sven Van Caekenberghe-2
5697 posts

> On 05 Feb 2015, at 15:44, Nicolai Hess <[hidden email]> wrote:
>
> 2015-02-02 3:03 GMT+01:00 Eliot Miranda <[hidden email]>:
>
>
> On Sun, Feb 1, 2015 at 3:39 AM, Nicolai Hess <[hidden email]> wrote:
>
>
> 2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:
>
> Looking into Image locking problems [1] caused by a recursive array such as this...
>
>     literalArray := #( 1 2 3 ).
>     literalArray at: 3 put: literalArray.
>
> I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...  
>
>
>
> Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.
>
> shouldBePrintedAsLiteralVisiting: aSet
>     self class == Array ifFalse:
>         [^false].
>     (aSet includes: self) ifTrue:
>         [^false].
>     aSet add: self.
>     ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
>
>
> isn't there a common pattern to handle this kind of potential endless recursion?
>
> At Cadence we fixed it thus:
>
> Object>>shouldBePrintedAsLiteral
>
> ^self isLiteral
>
> Array>>shouldBePrintedAsLiteral
>
> ^self class == Array
>  and: [self shouldBePrintedAsLiteralVisiting: (IdentitySet new: 8)]
>
> Object>>shouldBePrintedAsLiteralVisiting: aSet
>
> ^self isLiteral
>
>  Array>>shouldBePrintedAsLiteralVisiting: aSet
> self class == Array ifFalse:
> [^false].
> (aSet includes: self) ifTrue:
> [^false].
> aSet add: self.
> ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
>
>
> Is there something more "generic". Something we can use for any object tracing.
> Isn't there something the GC uses? The GC obviously does not fall into this loop.
> (It flags visited objects, but there is nothing exposed that can be used
> at the image level?)
> How do ImageSegment or Fuel work with recursive structures?

In Moose there is DeepTraverser which does something similar it seems.

FUEL & STON do this too.

> Nicolai
>
>  
>
>   Object>>isLiteral   ^false
>   Boolean>>isLiteral ^true
>   Character>>isLiteral ^true
>   Integer>>isLiteral ^true
>   String>>isLiteral ^true
>   UndefinedObject>>isLiteral ^true
>
>   ByteArray>>isLiteral ^self class == ByteArray
>   Float>>isLiteral ^self isFinite "^(self - self) = 0.0"
>   ScaledDecimal>>isLiteral ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]
>
>   Array>>isLiteral ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]
>
> ...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).  
>
>     Array>>printOn: aStream
> self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
> self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
> super printOn: aStream
>
> Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.
>
> Also, instead of a recursive Array>>isLiteral we'd have something like
>   LiteralArray>>isLiteral ^true
>   Array>>isLiteral ^false
> which seems to align much better with the pattern of the other #isLiteral implementors.
>
> I notice there is both RBArrayNode and RBLiteralArrayNode.
>
> So what are the wider concerns that might apply?
> (In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)
>
> cheers -ben
>
> [1] https://www.mail-archive.com/pharo-dev@.../msg25156.html
>
>
>
>
> --
> best,
> Eliot


Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Nicolai Hess
1347 posts
2015-02-05 16:02 GMT+01:00 Sven Van Caekenberghe <[hidden email]>:

> On 05 Feb 2015, at 15:44, Nicolai Hess <[hidden email]> wrote:
>
> 2015-02-02 3:03 GMT+01:00 Eliot Miranda <[hidden email]>:
>
>
> On Sun, Feb 1, 2015 at 3:39 AM, Nicolai Hess <[hidden email]> wrote:
>
>
> 2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:
>
> Looking into Image locking problems [1] caused by a recursive array such as this...
>
>     literalArray := #( 1 2 3 ).
>     literalArray at: 3 put: literalArray.
>
> I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...
>
>
>
> Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.
>
> shouldBePrintedAsLiteralVisiting: aSet
>     self class == Array ifFalse:
>         [^false].
>     (aSet includes: self) ifTrue:
>         [^false].
>     aSet add: self.
>     ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
>
>
> isn't there a common pattern to handle this kind of potential endless recursion?
>
> At Cadence we fixed it thus:
>
> Object>>shouldBePrintedAsLiteral
>
>       ^self isLiteral
>
> Array>>shouldBePrintedAsLiteral
>
>       ^self class == Array
>         and: [self shouldBePrintedAsLiteralVisiting: (IdentitySet new: 8)]
>
> Object>>shouldBePrintedAsLiteralVisiting: aSet
>
>       ^self isLiteral
>
>  Array>>shouldBePrintedAsLiteralVisiting: aSet
>       self class == Array ifFalse:
>               [^false].
>       (aSet includes: self) ifTrue:
>               [^false].
>       aSet add: self.
>       ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
>
>
> Is there something more "generic". Something we can use for any object tracing.
> Isn't there something the GC uses? The GC obviously does not fall into this loop.
> (It flags visited objects, but there is nothing exposed that can be used
> at the image level?)
> How do ImageSegment or Fuel work with recursive structures?

In Moose there is DeepTraverser which does something similar it seems.

FUEL & STON do this too.

How is it done in Fuel and STON ? Do they both use DeepTraverser, or
is there another implementation in Fuel and another one in STON?


 

> Nicolai
>
>
>
>   Object>>isLiteral           ^false
>   Boolean>>isLiteral          ^true
>   Character>>isLiteral                ^true
>   Integer>>isLiteral          ^true
>   String>>isLiteral           ^true
>   UndefinedObject>>isLiteral  ^true
>
>   ByteArray>>isLiteral                ^self class == ByteArray
>   Float>>isLiteral            ^self isFinite "^(self - self) = 0.0"
>   ScaledDecimal>>isLiteral    ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]
>
>   Array>>isLiteral            ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]
>
> ...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).
>
>     Array>>printOn: aStream
>       self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
>       self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
>       super printOn: aStream
>
> Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.
>
> Also, instead of a recursive Array>>isLiteral we'd have something like
>   LiteralArray>>isLiteral     ^true
>   Array>>isLiteral            ^false
> which seems to align much better with the pattern of the other #isLiteral implementors.
>
> I notice there is both RBArrayNode and RBLiteralArrayNode.
>
> So what are the wider concerns that might apply?
> (In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)
>
> cheers -ben
>
> [1] https://www.mail-archive.com/pharo-dev@.../msg25156.html
>
>
>
>
> --
> best,
> Eliot


Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Sven Van Caekenberghe-2
5697 posts

> On 16 Feb 2015, at 23:32, Nicolai Hess <[hidden email]> wrote:
>
> 2015-02-05 16:02 GMT+01:00 Sven Van Caekenberghe <[hidden email]>:
>
> > On 05 Feb 2015, at 15:44, Nicolai Hess <[hidden email]> wrote:
> >
> > 2015-02-02 3:03 GMT+01:00 Eliot Miranda <[hidden email]>:
> >
> >
> > On Sun, Feb 1, 2015 at 3:39 AM, Nicolai Hess <[hidden email]> wrote:
> >
> >
> > 2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:
> >
> > Looking into Image locking problems [1] caused by a recursive array such as this...
> >
> >     literalArray := #( 1 2 3 ).
> >     literalArray at: 3 put: literalArray.
> >
> > I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...
> >
> >
> >
> > Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.
> >
> > shouldBePrintedAsLiteralVisiting: aSet
> >     self class == Array ifFalse:
> >         [^false].
> >     (aSet includes: self) ifTrue:
> >         [^false].
> >     aSet add: self.
> >     ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
> >
> >
> > isn't there a common pattern to handle this kind of potential endless recursion?
> >
> > At Cadence we fixed it thus:
> >
> > Object>>shouldBePrintedAsLiteral
> >
> >       ^self isLiteral
> >
> > Array>>shouldBePrintedAsLiteral
> >
> >       ^self class == Array
> >         and: [self shouldBePrintedAsLiteralVisiting: (IdentitySet new: 8)]
> >
> > Object>>shouldBePrintedAsLiteralVisiting: aSet
> >
> >       ^self isLiteral
> >
> >  Array>>shouldBePrintedAsLiteralVisiting: aSet
> >       self class == Array ifFalse:
> >               [^false].
> >       (aSet includes: self) ifTrue:
> >               [^false].
> >       aSet add: self.
> >       ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
> >
> >
> > Is there something more "generic". Something we can use for any object tracing.
> > Isn't there something the GC uses? The GC obviously does not fall into this loop.
> > (It flags visited objects, but there is nothing exposed that can be used
> > at the image level?)
> > How do ImageSegment or Fuel work with recursive structures?
>
> In Moose there is DeepTraverser which does something similar it seems.
>
> FUEL & STON do this too.
>
> How is it done in Fuel and STON ? Do they both use DeepTraverser, or
> is there another implementation in Fuel and another one in STON?

They both have their own implementation. The actual traversal is not that difficult, doing it efficiently is a bit harder. Getting all the edge cases of the total algorithm (the serialisation) right is a more work.

I am not familiar with DeepTraverser, I just know it exists.

> > Nicolai
> >
> >
> >
> >   Object>>isLiteral           ^false
> >   Boolean>>isLiteral          ^true
> >   Character>>isLiteral                ^true
> >   Integer>>isLiteral          ^true
> >   String>>isLiteral           ^true
> >   UndefinedObject>>isLiteral  ^true
> >
> >   ByteArray>>isLiteral                ^self class == ByteArray
> >   Float>>isLiteral            ^self isFinite "^(self - self) = 0.0"
> >   ScaledDecimal>>isLiteral    ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]
> >
> >   Array>>isLiteral            ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]
> >
> > ...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).
> >
> >     Array>>printOn: aStream
> >       self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
> >       self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
> >       super printOn: aStream
> >
> > Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.
> >
> > Also, instead of a recursive Array>>isLiteral we'd have something like
> >   LiteralArray>>isLiteral     ^true
> >   Array>>isLiteral            ^false
> > which seems to align much better with the pattern of the other #isLiteral implementors.
> >
> > I notice there is both RBArrayNode and RBLiteralArrayNode.
> >
> > So what are the wider concerns that might apply?
> > (In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)
> >
> > cheers -ben
> >
> > [1] https://www.mail-archive.com/pharo-dev@.../msg25156.html
> >
> >
> >
> >
> > --
> > best,
> > Eliot


Reply | Threaded
Open this post in threaded view
| More
Print post
Permalink

Re: Proposal for LiteralArray class

Tudor Girba-2
7411 posts
Hi,

DeepTraverser is small (427 lines of code including comments and tests) and robust in that it deals with cycles, but it does not have support for limiting the depth of the search. Efficiency is not its strength, and it would be great if someone would review it.

I think something like this should be integrated in Pharo because it is so convenient for quick prototyping.

The original post that describes it is here:

Cheers,
Doru


On Mon, Feb 16, 2015 at 11:48 PM, Sven Van Caekenberghe <[hidden email]> wrote:

> On 16 Feb 2015, at 23:32, Nicolai Hess <[hidden email]> wrote:
>
> 2015-02-05 16:02 GMT+01:00 Sven Van Caekenberghe <[hidden email]>:
>
> > On 05 Feb 2015, at 15:44, Nicolai Hess <[hidden email]> wrote:
> >
> > 2015-02-02 3:03 GMT+01:00 Eliot Miranda <[hidden email]>:
> >
> >
> > On Sun, Feb 1, 2015 at 3:39 AM, Nicolai Hess <[hidden email]> wrote:
> >
> >
> > 2015-02-01 10:52 GMT+01:00 Ben Coman <[hidden email]>:
> >
> > Looking into Image locking problems [1] caused by a recursive array such as this...
> >
> >     literalArray := #( 1 2 3 ).
> >     literalArray at: 3 put: literalArray.
> >
> > I find that "literalArray printString" locks the image due to Array>>printOn: use of the recursive #shouldBePrintedAsLiteral method. Now its implementation is identical to #isLiteral and indeed "literalArray isLiteral" also locks the Image. So comparing implementors of #isLiteral...
> >
> >
> >
> > Squeak uses a Set to store all visited elements for shouldBePrintedAsLiteral and this protects against the recursive loop.
> >
> > shouldBePrintedAsLiteralVisiting: aSet
> >     self class == Array ifFalse:
> >         [^false].
> >     (aSet includes: self) ifTrue:
> >         [^false].
> >     aSet add: self.
> >     ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
> >
> >
> > isn't there a common pattern to handle this kind of potential endless recursion?
> >
> > At Cadence we fixed it thus:
> >
> > Object>>shouldBePrintedAsLiteral
> >
> >       ^self isLiteral
> >
> > Array>>shouldBePrintedAsLiteral
> >
> >       ^self class == Array
> >         and: [self shouldBePrintedAsLiteralVisiting: (IdentitySet new: 8)]
> >
> > Object>>shouldBePrintedAsLiteralVisiting: aSet
> >
> >       ^self isLiteral
> >
> >  Array>>shouldBePrintedAsLiteralVisiting: aSet
> >       self class == Array ifFalse:
> >               [^false].
> >       (aSet includes: self) ifTrue:
> >               [^false].
> >       aSet add: self.
> >       ^self allSatisfy: [:each | each shouldBePrintedAsLiteralVisiting: aSet]
> >
> >
> > Is there something more "generic". Something we can use for any object tracing.
> > Isn't there something the GC uses? The GC obviously does not fall into this loop.
> > (It flags visited objects, but there is nothing exposed that can be used
> > at the image level?)
> > How do ImageSegment or Fuel work with recursive structures?
>
> In Moose there is DeepTraverser which does something similar it seems.
>
> FUEL & STON do this too.
>
> How is it done in Fuel and STON ? Do they both use DeepTraverser, or
> is there another implementation in Fuel and another one in STON?

They both have their own implementation. The actual traversal is not that difficult, doing it efficiently is a bit harder. Getting all the edge cases of the total algorithm (the serialisation) right is a more work.

I am not familiar with DeepTraverser, I just know it exists.

> > Nicolai
> >
> >
> >
> >   Object>>isLiteral           ^false
> >   Boolean>>isLiteral          ^true
> >   Character>>isLiteral                ^true
> >   Integer>>isLiteral          ^true
> >   String>>isLiteral           ^true
> >   UndefinedObject>>isLiteral  ^true
> >
> >   ByteArray>>isLiteral                ^self class == ByteArray
> >   Float>>isLiteral            ^self isFinite "^(self - self) = 0.0"
> >   ScaledDecimal>>isLiteral    ^denominator = 1 or: [(10 raisedTo: scale)\\denominator = 0]
> >
> >   Array>>isLiteral            ^self class == Array and: [self allSatisfy: [:each | each isLiteral]]
> >
> > ...I find most are very basic (might even say deterministic), with the recursion of Array>>isLiteral seeming an annomaly.  Also, the big IF condition in Array>>printOn: smells like a design decision being made at runtime (Valloud AMCOS p12).
> >
> >     Array>>printOn: aStream
> >       self shouldBePrintedAsLiteral ifTrue: [self printAsLiteralFormOn: aStream. ^ self].
> >       self isSelfEvaluating ifTrue: [self printAsSelfEvaluatingFormOn: aStream. ^ self].
> >       super printOn: aStream
> >
> > Flipping between two printString formats seems like selecting between two class types. Indeed, if we had a LiteralArray class, there would be no need for its printOn: to recursively search to determine its form, thus allowing #printStringLimitedTo: to do its thing to protect against infinite recursion.
> >
> > Also, instead of a recursive Array>>isLiteral we'd have something like
> >   LiteralArray>>isLiteral     ^true
> >   Array>>isLiteral            ^false
> > which seems to align much better with the pattern of the other #isLiteral implementors.
> >
> > I notice there is both RBArrayNode and RBLiteralArrayNode.
> >
> > So what are the wider concerns that might apply?
> > (In particular, I'm not sure how the #isSelfEvaluating (which is also recursive) fits into the big picture)
> >
> > cheers -ben
> >
> > [1] https://www.mail-archive.com/pharo-dev@.../msg25156.html
> >
> >
> >
> >
> > --
> > best,
> > Eliot





--

"Every thing has its own flow"