Date   

Re: Bug in ProcessMonitor with multiple concurrent subprocesses?

 

What version of Pony are you using?

On Jan 28, 2018, at 12:07, Fionn Cleary <clearyf@tcd.ie> wrote:

Hi,

I think I've run into a bug in ProcessMonitor, based on the instructions
on the ponyc github re bugs I’ll just post this here so somebody else
can reproduce this prior to opening an issue on github.

I wrote a small program in pony to run a few (4 to be exact) processes
in parallel, parse the output, etc, and noticed that sometimes my
program hung while one of the subprocesses was stuck in the zombie
state. The zombie process corresponded to the ProcessNotifier that
didn’t have dispose called, so my code was hung waiting for dispose.
The code below reliably triggers the problem on my system here (Linux
x86_64, ponyc 0.21.3 from nix). The sample program starts a number of
`sleep 0.1` processes in parallel; the single command line argument is
the number of subprocesses to create. Testing like this:

for i in `seq 0 1000000`; do echo -n $i: ; ./result/bin/process_test 1; done

runs perfectly with 1 test process, so the problem does not seem to ever
trigger with a single subprocess:

999994:Started processes: All finished
999995:Started processes: All finished
999996:Started processes: All finished
999997:Started processes: All finished
999998:Started processes: All finished
999999:Started processes: All finished
1000000:Started processes: All finished

With 2 processes it generally takes somewhere between 500 to 1500
iterations for the program to hang, 3-4 < 50, and with > 10 subprocesses
it generally hangs each time. Note that with > 10 subprocesses there is
often more than one process stuck in the zombie state.

I brought this up on IRC earlier this week, although I was only able to
find some time over the weekend to distil my program down to something
that still demonstrates the problem.

Thanks,
Fionn

use "files"
use "process"

actor Main
let _env: Env
var _num_running: U64

new create(env: Env) =>
_env = env
_num_running = 0
try
let auth = _env.root as AmbientAuth
let path = FilePath(auth, "/usr/bin/sleep")?
var i: U64 = 0
while i < _env.args(1)?.u64()? do
start_process(auth, path)
i = i + 1
end
_num_running = i
_env.out.write("Started processes: ")
else
_env.out.print("Number of processes to start is a required argument!")
end

fun start_process(auth: AmbientAuth, path: FilePath) =>
let args = recover Array[String](2) end
args.push("sleep")
args.push("0.01")
let pm = ProcessMonitor(auth, auth, ProcessClient(_env, this), path, consume args, _env.vars())
pm.done_writing()

be finished_ok() =>
if _num_running > 0 then
_num_running = _num_running - 1
if _num_running == 0 then
_env.out.print("All finished")
end
else
_env.out.print("Got finished_ok, but everything is already finished!?")
end

class ProcessClient is ProcessNotify
let _env: Env
let _main: Main

new iso create(env: Env, main: Main) =>
_env = env
_main = main

fun ref stdout(process: ProcessMonitor, data: Array[U8] iso) =>
None

fun ref stderr(process: ProcessMonitor, data: Array[U8] iso) =>
None

fun ref failed(process: ProcessMonitor, err: ProcessError) =>
match err
| ExecveError => _env.out.print("ProcessError: ExecveError")
| PipeError => _env.out.print("ProcessError: PipeError")
| ForkError => _env.out.print("ProcessError: ForkError")
| WaitpidError => _env.out.print("ProcessError: WaitpidError")
| WriteError => _env.out.print("ProcessError: WriteError")
| KillError => _env.out.print("ProcessError: KillError")
| CapError => _env.out.print("ProcessError: CapError")
| Unsupported => _env.out.print("ProcessError: Unsupported")
end

fun ref dispose(process: ProcessMonitor, child_exit_code: I32) =>
if child_exit_code != 0 then
_env.out.print("Child exit code: " + child_exit_code.string())
end
_main.finished_ok()



Last Week in Pony - January 28, 2018

 

Dear Community,

i hereby present you the latest Last Week in Pony post, fresh from the tap:

https://www.ponylang.org/blog/2018/01/last-week-in-pony---january-28-2018/

Don't spill it!

kthxbye!

Matthias


Bug in ProcessMonitor with multiple concurrent subprocesses?

Fionn Cleary
 

Hi,

I think I've run into a bug in ProcessMonitor, based on the instructions
on the ponyc github re bugs I’ll just post this here so somebody else
can reproduce this prior to opening an issue on github.

I wrote a small program in pony to run a few (4 to be exact) processes
in parallel, parse the output, etc, and noticed that sometimes my
program hung while one of the subprocesses was stuck in the zombie
state. The zombie process corresponded to the ProcessNotifier that
didn’t have dispose called, so my code was hung waiting for dispose.
The code below reliably triggers the problem on my system here (Linux
x86_64, ponyc 0.21.3 from nix). The sample program starts a number of
`sleep 0.1` processes in parallel; the single command line argument is
the number of subprocesses to create. Testing like this:

for i in `seq 0 1000000`; do echo -n $i: ; ./result/bin/process_test 1; done

runs perfectly with 1 test process, so the problem does not seem to ever
trigger with a single subprocess:

999994:Started processes: All finished
999995:Started processes: All finished
999996:Started processes: All finished
999997:Started processes: All finished
999998:Started processes: All finished
999999:Started processes: All finished
1000000:Started processes: All finished

With 2 processes it generally takes somewhere between 500 to 1500
iterations for the program to hang, 3-4 < 50, and with > 10 subprocesses
it generally hangs each time. Note that with > 10 subprocesses there is
often more than one process stuck in the zombie state.

I brought this up on IRC earlier this week, although I was only able to
find some time over the weekend to distil my program down to something
that still demonstrates the problem.

Thanks,
Fionn

use "files"
use "process"

actor Main
let _env: Env
var _num_running: U64

new create(env: Env) =>
_env = env
_num_running = 0
try
let auth = _env.root as AmbientAuth
let path = FilePath(auth, "/usr/bin/sleep")?
var i: U64 = 0
while i < _env.args(1)?.u64()? do
start_process(auth, path)
i = i + 1
end
_num_running = i
_env.out.write("Started processes: ")
else
_env.out.print("Number of processes to start is a required argument!")
end

fun start_process(auth: AmbientAuth, path: FilePath) =>
let args = recover Array[String](2) end
args.push("sleep")
args.push("0.01")
let pm = ProcessMonitor(auth, auth, ProcessClient(_env, this), path, consume args, _env.vars())
pm.done_writing()

be finished_ok() =>
if _num_running > 0 then
_num_running = _num_running - 1
if _num_running == 0 then
_env.out.print("All finished")
end
else
_env.out.print("Got finished_ok, but everything is already finished!?")
end

class ProcessClient is ProcessNotify
let _env: Env
let _main: Main

new iso create(env: Env, main: Main) =>
_env = env
_main = main

fun ref stdout(process: ProcessMonitor, data: Array[U8] iso) =>
None

fun ref stderr(process: ProcessMonitor, data: Array[U8] iso) =>
None

fun ref failed(process: ProcessMonitor, err: ProcessError) =>
match err
| ExecveError => _env.out.print("ProcessError: ExecveError")
| PipeError => _env.out.print("ProcessError: PipeError")
| ForkError => _env.out.print("ProcessError: ForkError")
| WaitpidError => _env.out.print("ProcessError: WaitpidError")
| WriteError => _env.out.print("ProcessError: WriteError")
| KillError => _env.out.print("ProcessError: KillError")
| CapError => _env.out.print("ProcessError: CapError")
| Unsupported => _env.out.print("ProcessError: Unsupported")
end

fun ref dispose(process: ProcessMonitor, child_exit_code: I32) =>
if child_exit_code != 0 then
_env.out.print("Child exit code: " + child_exit_code.string())
end
_main.finished_ok()


Re: Pony Cassandra Client

Jesse Cooke
 

Diesel could be good inspiration http://diesel.rs/


On Wed, Jan 24, 2018, 08:25 James Bracy <waratuman@...> wrote:
PostgreSQL would be another one that I would like to see. Really though I think a SQL AST manager would be great (similar to https://github.com/rails/arel). This is kind of the reason I stopped as I would have to continue to to do all these things. I love building them and writing in Pony but the timeframe didn't allow me to continue.


On Wed, Jan 24, 2018 at 12:25 AM, Matthias Wahl <pony@...> wrote:

Hi James, very nice, enabling cassandra connections for pony!

One question: What clients have you been interested in? Knowledge of what people want to use pony for is really helpful when deciding on the next spare-time project ;)

kthxbye! Matthias



Re: Pony Cassandra Client

 

PostgreSQL would be another one that I would like to see. Really though I think a SQL AST manager would be great (similar to https://github.com/rails/arel). This is kind of the reason I stopped as I would have to continue to to do all these things. I love building them and writing in Pony but the timeframe didn't allow me to continue.


On Wed, Jan 24, 2018 at 12:25 AM, Matthias Wahl <pony@...> wrote:

Hi James, very nice, enabling cassandra connections for pony!

One question: What clients have you been interested in? Knowledge of what people want to use pony for is really helpful when deciding on the next spare-time project ;)

kthxbye! Matthias



Re: Pony Cassandra Client

 

Hi James, very nice, enabling cassandra connections for pony!

One question: What clients have you been interested in? Knowledge of what people want to use pony for is really helpful when deciding on the next spare-time project ;)

kthxbye! Matthias


Pony Cassandra Client

 

Hi everyone!

A few months ago I started developing a pony client for Cassandra with the intention of using it in one of my applications. I switched languages mid-way simply because I didn't have the time to write clients for everything I wanted to interface with. The client can currently connect and I've sent some very simple queries with it. Not sure if it would be of interest to anyone here but I figured someone might find it useful if they are building a binary interface.

You can view the project here: https://github.com/waratuman/pony-cql

Also want to say thank you to everyone who is developing Pony itself! I love the ideas behind it and really consider it a next generation language. After getting used to the type system(s) I enjoyed it very much. 

Cheers!
- James


Last Week in Pony - January 21, 2018

 

On the verge of the upcoming week, on the brink of collapse of the old one, in between two completely different worlds. A state of transition, between not yet and no more, impossible to grasp, it leaves us in a state of wonder. Do you see all the paths that led you here and all the possibilities that lie ahead of you at this crossroad of destiny?

Born from this ever repeating, still never the same, mythical period, a new Last Week in Pony is out:

Last Week in Pony - January 21, 2018

Do you feel the sizzle? I certainly do!

kthxbye!

Matthias


Re: Implementing a state machine in pony

Wink Saville
 

Txs, in my case it will never "error" so "try" should be faster. I think I'll make some measurements.


On Sat, Jan 20, 2018, 7:02 PM Sean T. Allen <sean@...> wrote:

On Sat, Jan 20, 2018 at 7:04 PM, Wink Saville <wink@...> wrote:
Adding _set_machine doesn't really help, now the State._sm field needs to change to a var:

  var _sm: (StateMachine | None) = None

And then I'd need to use "try" or "match" everywhere I use _sm in each State, that seems even worse
as there is one "StateMachine" and many "State" objects.

What about "try" vs "match" (or something else), is there a preference from size/speed/style trade offs?



Re: Implementing a state machine in pony

 

On Sat, Jan 20, 2018 at 7:04 PM, Wink Saville <wink@...> wrote:
Adding _set_machine doesn't really help, now the State._sm field needs to change to a var:

  var _sm: (StateMachine | None) = None

And then I'd need to use "try" or "match" everywhere I use _sm in each State, that seems even worse
as there is one "StateMachine" and many "State" objects.

What about "try" vs "match" (or something else), is there a preference from size/speed/style trade offs?



Re: Implementing a state machine in pony

Wink Saville
 

Adding _set_machine doesn't really help, now the State._sm field needs to change to a var:

  var _sm: (StateMachine | None) = None

And then I'd need to use "try" or "match" everywhere I use _sm in each State, that seems even worse
as there is one "StateMachine" and many "State" objects.

What about "try" vs "match" (or something else), is there a preference from size/speed/style trade offs?


Re: Pony CBOR Implementation

Murray Calavera
 

Hello Sean,

Are you familiar with the Pony library starter pack?
Yes, I'll look thought it at some point and see what I need.

Your formatting isn't the same standard as the Pony standard library.
I read through the guide but I find all caps for things like JSON and
CBOR in type names
quite unnatural. But I suppose following standards is more important,
perhaps I should change it.

CBorHash.eq certainly seems rather odd when you first glance at it.
Could you explain?
The CBOR encoding has a map type that allows any other type as a key,
this means everything
needs to have an equality relation to everything else.
Types are generally disjoint.
Tags however are just ignored.
Numbers are converted to I128 in order to compare any two numeric
types (not sure this is the best way.)
Maps as keys are disjoint from everything including other maps.

What about _read_negative feels awkward to you?
I have to do this I128 conversion dance to prevent overflow, but I
guess it's fine.

Do CBorArray, CBorMap etc exist because of
https://github.com/ponylang/ponyc/issues/267 ?
Yes.


I actually ran my tests on a different machine just now(arch linux
amd64, pony 0.21.2) and discovered that some of them fail.
I think it's a bug, the array unshift function simply overwrites index
0 without shifting when called for a second time.
Happens only with optimizations turned on. I'll open an issue later
after some investigation and testing with latest pony.

Thanks for taking the time to look at my code!
Murray.

On 19 January 2018 at 22:04, Sean T. Allen <sean@monkeysnatchbanana.com> wrote:
Hi Murray,

Are you familiar with the Pony library starter pack? It contains a number of
things including easy to install CircleCI setup that you might be interested
in: https://github.com/ponylang/library-project-starter/blob/master/USAGE.md

________________________________

Your formatting isn't the same standard as the Pony standard library. That
is fine, but if you weren't aware of that, you can find the style guide
here: https://github.com/ponylang/ponyc/blob/master/STYLE_GUIDE.md

________________________________

You use (U8 | U16 | U32 | U64) as the type in a number of locations. Your
life might be easier if you defined a type alias for that:

type UnsignedCBOR is (U8 | U16 | U32 | U64)

or something like that

________________________________

CBorHash.eq certainly seems rather odd when you first glance at it. I don't
really understand the logic of it. Could you explain?

________________________________

What about _read_negative feels awkward to you?

________________________________

Do CBorArray, CBorMap etc exist because of
https://github.com/ponylang/ponyc/issues/267 ? They seem to have limited
utility otherwise.

-Sean-


Re: Implementing a state machine in pony

 

Your problem is:

state1= State1(this)

that's unsafe. the state machine hasn't been initialized yet so you can hand out a `this` reference. You can either rethink State's knowing about the StateMachine that contains them or you can move it out of their construction... create a State, initialize a state.

state1 = State1
state2 = State2
state1._set_machine(this)
state2._set_machine(this)

the same applies to cur_state but its a little different.
currently you can't call any methods on the object until after everything is initialized.

so you can do

cur_state = state1

in the constructor and everything will be happy.

you can call `tag` functions in the constructor but that doesn't apply here. (Tag can't read or write fields so its safe to call on a non-fully initialized object).





Implementing a state machine in pony

Wink Saville
 

In the code below we have a main actor which has a StateMachine object and
the StateMachine oject has 2 child states. StateMachine.transitionTo is used to
move between the states and StateMachine.doIt dispatches to the current
child state.
```
 
 
     1 type States is (State1 | State2)
     2
     3 class StateMachine
     4   let env: Env
     5   var state1: (State1 | None) = None
     6   var state2: (State2 | None) = None
     7   var cur_state: (States | None) = None
     8
     9   new create(env': Env) =>
    10     env = env'
    11     state1 = State1(this)
    12     state2 = State2(this)
    13     transitionTo(state1)
    14
    15   fun ref transitionTo(state: (States | None)) =>
    16     cur_state = state
    17
    18   fun ref doIt() =>
    19     if true then
    20       try
    21         (cur_state as States).doIt()
    22       end
    23     else
    24       match cur_state
    25       | let c: States => c.doIt()
    26       end
    27     end
    28
    29
    30 class State1
    31   let _sm: StateMachine
    32
    33   new create(sm: StateMachine) =>
    34     _sm = sm
    35
    36   fun ref doIt() =>
    37     _sm.env.out.print("State1.doIt()")
    38     _sm.transitionTo(_sm.state2)
    39
    40
    41 class State2
    42   let _sm: StateMachine
    43
    44   new create(sm: StateMachine) =>
    45     _sm = sm
    46
    47   fun ref doIt() =>
    48     _sm.env.out.print("State2.doIt()")
    49     _sm.transitionTo(_sm.state1)
    50
    51
    52 actor Main
    53   var sm: StateMachine
    54
    55   new create(env: Env) =>
    56     sm = StateMachine(env)
    57     doIt()
    58
    59   be doIt() =>
    60     sm.doIt()
    61     sm.doIt()
    62     sm.doIt()
    63     sm.doIt()
```
The above code compiles and runs just fine, but it doesn't express my
intentions very well. In particular, StateMachine.child1 and StateMachine.child2
are immutable. They are initialized in StateMachine.create and never change.
Also, StateMachine.cur_state will never be None after create, it will only be one
of the States.

If I could inform the compiler of my intentions, than using the match or try would
be unnecessary in StateMachine.doIt and over all the code would probably be
smaller and faster.

So in an ideal world StateMachine would be something like:
```
     3 class StateMachine
     4   let env: Env
     5   let state1: State1
     6   let state2: State2
     7   var cur_state: States
     8
     9   new create(env': Env) =>
    10     env = env'
    11     state1 = State1(this)
    12     state2 = State2(this)
    13     transitionTo(state1)
    14
    15   fun ref transitionTo(state: (States | None)) =>
    16     cur_state = state
    17
    18   fun ref doIt() =>
    19            cur_state.doIt()
```
Any advice on how I might improve the code to be smaller, faster or more idomatic
pony would be great. But most of all I'd like to be able to express my intention better.


Re: Why does fun need ref capability

Wink Saville
 

Benoit, that seems perfect what is the downside?


Re: Why does fun need ref capability

 

Hi Wink,
Eric and Sean already explained why your code doesn't work, so I'll add to their explanations with a way to make your code work while keeping `getCounter` as a `box` method.
What you can do is change the return type from `Counter ref` to `this->Counter ref`, i.e. `Counter ref` as seen by `this`. This means that the type will be adapted depending on the capability of the receiver. If you call the method on a `box` receiver, you'll get a `box->Counter ref` (i.e. `Counter box`). For a `ref` receiver you'll get a `ref->Counter ref` (i.e. `Counter ref`) and for a `val` receiver you'll get a `val->Counter ref` (i.e. `Counter val`).

Benoit


Re: Why does fun need ref capability

Wink Saville
 

Eric/Sean, thanks for the explanation, it makes sense. But in my opinion the compiler error didn't help me at all.

On the other hand given this code:
class val Counter
  var _count: U64 = 0

  fun count(): U64 =>
    _count
    
  fun /*ref*/ inc(): U64 =>
    _count = _count + 1
    _count

actor Main
  new create(env: Env) =>
    var c: Counter ref = Counter
    env.out.print(c.count().string())
    env.out.print(c.inc().string())

This error message is helpful:

0.21.3 [release]
compiled with: llvm 3.9.1 -- cc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
Error:
main.pony:8:12: cannot write to a field in a box function. If you are trying to change state in a function use fun ref
    _count = _count + 1
           ^


This would work:


The key here is that Counter in your example defaults to a `ref`. Its mutable.

Your counter is mutable, its a field on HasCounter so you have to have a mutable ref to HasCounter to get access to Counter as by having access to counter, you'd be able to mutate HasCounter's state by calling `inc` on it.

If you were allowed to called getCounter() via a val then you could send val copies of HasCounter to different actors. Each actor could get getCounter() and have a mutable reference to counter. That would be unsafe.

Thus, to call getCounter, you have to have a `ref` of HasCounter.

-Sean-


On Fri, Jan 19, 2018 at 3:13 PM, Wink Saville <wink@...> wrote:
In the following code the declaration of HasCounter.getCounter() must be declared as a ref, why?
Apparently the reason is on line 12 we have "let _counter: Counter = Counter" and the compiler
has decided it defines a "Counter box". This doesn't make sense, Counter is explicitly
declared a ref, although it unnecessary as the default is ref for classes, but I thought I'd add it
just in case. Explanation appreciated :)

Here, https://is.gd/PnjGXt, is the code in playground:
class ref Counter
  var _count: U64 = 0

  fun count(): U64 =>
    _count
    
  fun ref inc(): U64 =>
    _count = _count + 1
    _count

class ref HasCounter
  let _counter: Counter = Counter
  
  // Why does this have to be ref
  fun /*ref*/ getCounter(): Counter =>
    _counter

actor Main
  new create(env: Env) =>
    var hasCounter: HasCounter = HasCounter
    env.out.print(hasCounter.getCounter().count().string())
    env.out.print(hasCounter.getCounter().inc().string())


Here is the compiler output:
0.21.3 [release]
compiled with: llvm 3.9.1 -- cc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
Error:
main.pony:16:5: function body isn't the result type
    _counter
    ^
    Info:
    main.pony:15:29: function return type: Counter ref
      fun /*ref*/ getCounter(): Counter =>
                                ^
    main.pony:16:5: function body type: this->Counter ref
        _counter
        ^
    main.pony:12:17: Counter box is not a subtype of Counter ref: box is not a subcap of ref
      let _counter: Counter = Counter



Re: Pony CBOR Implementation

 

Hi Murray,

Are you familiar with the Pony library starter pack? It contains a number of things including easy to install CircleCI setup that you might be interested in: https://github.com/ponylang/library-project-starter/blob/master/USAGE.md


Your formatting isn't the same standard as the Pony standard library. That is fine, but if you weren't aware of that, you can find the style guide here: https://github.com/ponylang/ponyc/blob/master/STYLE_GUIDE.md


You use (U8 | U16 | U32 | U64) as the type in a number of locations. Your life might be easier if you defined a type alias for that:

type UnsignedCBOR is (U8 | U16 | U32 | U64)

or something like that


CBorHash.eq certainly seems rather odd when you first glance at it. I don't really understand the logic of it. Could you explain?


What about _read_negative feels awkward to you?


Do CBorArray, CBorMap etc exist because of https://github.com/ponylang/ponyc/issues/267 ? They seem to have limited utility otherwise.

-Sean-


Re: Why does fun need ref capability

 

Wink,

This would work:


The key here is that Counter in your example defaults to a `ref`. Its mutable.

Your counter is mutable, its a field on HasCounter so you have to have a mutable ref to HasCounter to get access to Counter as by having access to counter, you'd be able to mutate HasCounter's state by calling `inc` on it.

If you were allowed to called getCounter() via a val then you could send val copies of HasCounter to different actors. Each actor could get getCounter() and have a mutable reference to counter. That would be unsafe.

Thus, to call getCounter, you have to have a `ref` of HasCounter.

-Sean-


On Fri, Jan 19, 2018 at 3:13 PM, Wink Saville <wink@...> wrote:
In the following code the declaration of HasCounter.getCounter() must be declared as a ref, why?
Apparently the reason is on line 12 we have "let _counter: Counter = Counter" and the compiler
has decided it defines a "Counter box". This doesn't make sense, Counter is explicitly
declared a ref, although it unnecessary as the default is ref for classes, but I thought I'd add it
just in case. Explanation appreciated :)

Here, https://is.gd/PnjGXt, is the code in playground:
class ref Counter
  var _count: U64 = 0

  fun count(): U64 =>
    _count
    
  fun ref inc(): U64 =>
    _count = _count + 1
    _count

class ref HasCounter
  let _counter: Counter = Counter
  
  // Why does this have to be ref
  fun /*ref*/ getCounter(): Counter =>
    _counter

actor Main
  new create(env: Env) =>
    var hasCounter: HasCounter = HasCounter
    env.out.print(hasCounter.getCounter().count().string())
    env.out.print(hasCounter.getCounter().inc().string())


Here is the compiler output:
0.21.3 [release]
compiled with: llvm 3.9.1 -- cc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
Error:
main.pony:16:5: function body isn't the result type
    _counter
    ^
    Info:
    main.pony:15:29: function return type: Counter ref
      fun /*ref*/ getCounter(): Counter =>
                                ^
    main.pony:16:5: function body type: this->Counter ref
        _counter
        ^
    main.pony:12:17: Counter box is not a subtype of Counter ref: box is not a subcap of ref
      let _counter: Counter = Counter



Re: Why does fun need ref capability

 

Hello. I'll try to explain, but since I'm getting familiar with Pony too, my explanation may fall short.

By default, all function declarations have the box capabilities. (Similarly to how behaviours and actors have tag capabilities, and classes have ref capabilities by default)

What this means is that, when defining a function, if you do not specify a capability, it will default to a box origin. In other words, when the getCounter() method is called, the HasCounter object will be expected to be a box (or otherwise, will be subcapped to one), while still maintaining a _counter ref field. This is what your "HasCounter" class looks like with explicit reference capabilities:

class ref HasCounter
  let _counter: Counter ref = Counter

  fun box getCounter(): Counter ref =>
    _counter

From the "Combining Capabilities" section of the tutorial, in "Viewpoint adaptation", you can see that calling a ref field from a box origin will return a box variable. This means that there will be a mismatch between the declared return type (Counter ref) and the actual return type (Counter box). Since box is not a subcap of ref, this errors out.

On the other hand, when you have a ref origin (by explicitly including the capability in the function definition), and a ref field, it correctly subcaps to the Counter ref expected in the return type.

Anyway, I hope you could follow this explanation. If not, please give me a shout.

Cheers,

Eric.

321 - 340 of 1896