Skip to content

Commit 7b51c58

Browse files
committed
Hylolib: Alternative, statically safe Slice design
1 parent d5433c5 commit 7b51c58

File tree

4 files changed

+62
-28
lines changed

4 files changed

+62
-28
lines changed

tests/pos/hylolib/AnyCollection.scala

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,8 @@ given [T: Value] => AnyCollection[T] is Collection:
4848
type Position = AnyValue
4949

5050
extension (self: AnyCollection[T])
51-
52-
def startPosition =
53-
self._start()
54-
55-
def endPosition =
56-
self._end()
57-
58-
def positionAfter(p: Position) =
59-
self._after(p)
60-
61-
def at(p: Position) =
62-
self._at(p)
51+
def startPosition = self._start()
52+
def endPosition = self._end()
53+
def positionAfter(p: Position) = self._after(p)
54+
def at(p: Position) = self._at(p)
6355

tests/pos/hylolib/AnyValue.scala

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,7 @@ object AnyValue {
6161
given AnyValue is Value:
6262

6363
extension (self: AnyValue)
64-
65-
def copy(): AnyValue =
66-
self.copy()
67-
68-
def eq(other: AnyValue): Boolean =
69-
self `eq` other
70-
71-
def hashInto(hasher: Hasher): Hasher =
72-
self.hashInto(hasher)
64+
def copy(): AnyValue = self.copy()
65+
def eq(other: AnyValue): Boolean = self `eq` other
66+
def hashInto(hasher: Hasher): Hasher = self.hashInto(hasher)
7367

tests/pos/hylolib/Collection.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ trait Collection:
7878
else if n `eq` e then false
7979
else recur(self.positionAfter(n))
8080
recur(self.positionAfter(i))
81+
82+
class Slice2(val base: Self, val bounds: Range[Position]):
83+
84+
def isEmpty: Boolean =
85+
bounds.lowerBound.eq(bounds.upperBound)
86+
87+
def startPosition: Position =
88+
bounds.lowerBound
89+
90+
def endPosition: Position =
91+
bounds.upperBound
92+
93+
def at(p: Position): Element =
94+
base.at(p)
95+
end Slice2
96+
8197
end Collection
8298

8399
extension [Self: Collection](self: Self)
@@ -97,6 +113,15 @@ extension [Self: Collection](self: Self)
97113
val t = Slice(self, Range(q, self.endPosition, (a, b) => (a `eq` b) || self.isBefore(a, b)))
98114
Some((self.at(p), t))
99115

116+
def headAndTail2: Option[(Self.Element, Self.Slice2)] =
117+
if self.isEmpty then
118+
None
119+
else
120+
val p = self.startPosition
121+
val q = self.positionAfter(p)
122+
val t = Self.Slice2(self, Range(q, self.endPosition, (a, b) => (a `eq` b) || self.isBefore(a, b)))
123+
Some((self.at(p), t))
124+
100125
/** Applies `combine` on `partialResult` and each element of `self`, in order.
101126
*
102127
* @complexity

tests/pos/hylolib/Slice.scala

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package hylo
22

33
/** A view into a collection. */
44
final class Slice[Base: Collection](
5-
tracked val base: Base,
6-
tracked val bounds: Range[Base.Position]
5+
val base: Base,
6+
val bounds: Range[Base.Position]
77
) {
88

99
/** Returns `true` iff `this` is empty. */
@@ -24,17 +24,40 @@ final class Slice[Base: Collection](
2424

2525
}
2626

27-
given [T: Collection as c] => Slice[T] is Collection:
27+
given [C: Collection] => Slice[C] is Collection:
2828

29-
type Element = c.Element
30-
type Position = c.Position
29+
type Element = C.Element
30+
type Position = C.Position
3131

32-
extension (self: Slice[T])
32+
extension (self: Slice[C])
3333

34-
def startPosition = self.bounds.lowerBound.asInstanceOf[Position] // NOTE: Ugly hack
34+
def startPosition = self.bounds.lowerBound.asInstanceOf[Position]
35+
// This is actually unsafe. We have:
36+
// self.bounds: Range(Slice[C].Base.Position)
37+
// But the _value_ of Slice[C].Base is not necssarily this given, even
38+
// though it is true that `type Slice[C].Base = C`. There might be multiple
39+
// implementations of `Slice[C] is Collection` that define different `Position`
40+
// types. So we cannot conclude that `Slice[C].Base.Position = this.Position`.
41+
// To make this safe, we'd need some form of coherence, where we ensure that
42+
// there is only one way to implement `Slice is Collection`.
43+
//
44+
// As an alternativem we can make Slice dependent on the original Collection
45+
// _instance_ instead of the original Collection _type_. This design is
46+
// realized by the Slice2 definitions. It works without casts.
3547

3648
def endPosition = self.bounds.upperBound.asInstanceOf[Position]
3749

3850
def positionAfter(p: Position) = self.base.positionAfter(p)
3951

4052
def at(p: Position) = self.base.at(p)
53+
54+
given [C: Collection] => C.Slice2 is Collection:
55+
type Element = C.Element
56+
type Position = C.Position
57+
58+
extension (self: C.Slice2)
59+
60+
def startPosition = self.bounds.lowerBound
61+
def endPosition = self.bounds.upperBound
62+
def positionAfter(p: Position) = self.base.positionAfter(p)
63+
def at(p: Position) = self.base.at(p)

0 commit comments

Comments
 (0)