Replies: 5 comments 10 replies
-
Like you said, we are taking about two programming paradigms:
At the point of Law of exclusivity says that an object can be mutated only by one part of the code, and while somebody is mutating the object, nobody else can have access to it. That is:
A Hylo, imposed rules to prevent these things to happen (on regular, non-unsafe, code).
That being said, there are cases in which one need to implement shared caches, in which multiple actors need to read and update, without upholding the law of exclusivity. These can be written by using Following Sean's advice from the video posted above, we should write abstractions for these higher level constructs, and not necessarily for shared pointers. Hope that makes sense. |
Beta Was this translation helpful? Give feedback.
-
I don't think we've left anything important out.
That's an anti-pattern, and supporting it is part of the poor trade-off that C++ non-destructive move semantics makes. Not supporting this directly in safe code is an intentional part of Hylo's design. After the call, one has to assume that the value was consumed anyway, so one might as well pass it by |
Beta Was this translation helpful? Give feedback.
-
That's not quite precise; the key word is can. Dereferencing a |
Beta Was this translation helpful? Give feedback.
-
We share your concern about this.
We don't believe there are useful grades in the middle. "A little unsafe" is still unsafe; it allows undefined behavior, which means it allows everything no matter how bad. What we do acknowledge is that entire types are not necessarily unsafe; copying a |
Beta Was this translation helpful? Give feedback.
-
That's all true but it's an additional language wrinkle for an extremely marginal use-case and a pattern that we want to discourage. On balance it's better to say "no" to features like that, to keep language complexity down.
I think they're not just unimportant; they're more harmful than helpful. If you need something done at the end of a scope, there should be a first-class, explicit way of saying so, rather than piggybacking the functionality on destruction semantics, whose exact timing almost never matters in practice. Again, it's a trade-off. |
Beta Was this translation helpful? Give feedback.
-
At some point there will be a need to write bridges to code written in different paradigms. One of the things that will be needed is heap objects and ownership management of them.
I might be completely wrong, but IMO Hylo's value semantics is almost ready to express concepts of UniquePtr and SharedPtr, but not quite yet.
I'd like to discuss approaches to this.
SharedPtr
For SharedPtr it seems everything should be fine.
SharedPtr
type is handling a side table, in which we store a reference counter and the destination pointer. When SharedPtr is copied it increases the refcounter, when it's deinited it decreases the refcounter, when refcounter reaches 0 - releases the destintion resource.WeakPtr
is pretty much the same.One thing that bothers me is the lack of guarantees about deinit calls. IIUC exit of a variable's lexical scope is not a guaranteed point for deinit call. It could be postponed or be called earlier. It's more or less acceptable, but lexical lifetimes make it easier to reason about lifetime of a resource behind
SharedPtr
.UniquePtr
UniquePtr
could be expressed as a type with only theMoveable
trait implemented, and theCopyable
trait is intentionally dropped. But move-only types are pretty much impossible to work with when we don't know at compile time will the value sink or not.We can't know what passing convention to use here, as it depends on runtime random.
We can overcome this by returning
Optional<UniquePtr>
withnil
ifx
was actually sinked, or with originalx
if it wasn't.But this approach has two inconvenient consequences:
Optionals
of unused parameters passed bysink
. And it has kinda viral effect.sink
parameterx
and the returned from the callOptional<UniquePtr>
are the same value.A solution I came up with is a new parameter passing convention and a type of binding -
sinkable
. It's likesink
but if by the end of lifetime of a binding it was not used - a special block of code will be invoked. And in this block the source of the binding is restored.A
sinkable
value can be a result of a subscript (or function) call. In this case the restore block is invoked as part of the coroutine continuation.*Note: In this examples:
sink X restore { ... }
is expression that produces asinkable
binding fromX
yield_sinkable
is just like the regular yield for inout, but yieldssinkable
binding.With this type of binding we can express a method for
Optional
, thatyield
itself by mutating tonil
, and if the yielded binding wasn't sinked - restores its state.Usage example:
Beta Was this translation helpful? Give feedback.
All reactions