Task Escape

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

Task Escape

Zulq Alam-2
I have a task with N steps:

MyTask>>#go
        self stepOne.
        self stepTwo.
        ...
        self stepN.

Each step calls one or more components. Each component offer a Cancel
link / button. If clicked, the component answers false. If a component
answers false, the task should stop. I currently do this by checking the
answer value and then answering if the value is false.

MyTask>>#stepN
        (self call: MyComponent new)
                ifFalse: [self answer: false]

This seems to work OK but only if the task has been called. If it is the
root component, then the answer is a no-op.

I don't intend for such tasks to be used as root components and
practically this isn't a problem but it doesn't feel right. The task
should work no matter how it is being used.

I suppose I could check after each step (yuk):

MyTask>>#go
        ((self stepOne
                ifTrue: [self stepTwo])
                        ifTrue: [self stepThree])
        ...
                        ifTrue: [self stepN]

Or, I could check at the beginning of each step (*slightly* less yuk):

MyTask>>#stepN
        shouldRun ifFalse: [^ self].
        ...

Or:

MyTask>>#stepN
        self stepNMinusOne ifFalse: [^ self]
        ...

Or I could use a signal:

MyTask>>#stepN
        (self call: MyComponent new) ifFalse: [MyCancellation signal]

MyTask>>#go
        [self stepOne.
        self stepTwo.
        ...
        self stepThree]
                on: MyCancellation
                do: [self answer: false]

To me the signal looks the best, but feels like an abuse of the
exception handling framework.

What have others done? It's very possible I still don't know how to use
tasks properly, so please feel free to correct me!

Thanks,
Zulq.

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Task Escape

keith1y
or... you could try a message eating null. I know that people are alway
suspicious of letting message eating null loose in their code, however I
do think that in a specific controlled situation it can be very useful.

You example becomes...

self stepOne stepTwo stepThree.

if any of the steps returns Null then the subsequent steps are passed over.

Null is available in universes.

cheers

Keith
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Task Escape

Philippe Marschall
In reply to this post by Zulq Alam-2
2008/1/9, Zulq Alam <[hidden email]>:

> I have a task with N steps:
>
> MyTask>>#go
>         self stepOne.
>         self stepTwo.
>         ...
>         self stepN.
>
> Each step calls one or more components. Each component offer a Cancel
> link / button. If clicked, the component answers false. If a component
> answers false, the task should stop. I currently do this by checking the
> answer value and then answering if the value is false.
>
> MyTask>>#stepN
>         (self call: MyComponent new)
>                 ifFalse: [self answer: false]
>
> This seems to work OK but only if the task has been called. If it is the
> root component, then the answer is a no-op.

Right but, instead of #answer: ing you could as well #call: something like:

MyTask>>#stepN
        (self call: MyComponent new)
                ifFalse: [ self call: CanceledComponent new ]

It's probably best to encapsulate such logic in an own method:

MyTask>>#done
        self call: CanceledComponent new

MyTask>>#stepN
        (self call: MyComponent new)
                ifFalse: [ self done ]

> I don't intend for such tasks to be used as root components and
> practically this isn't a problem but it doesn't feel right. The task
> should work no matter how it is being used.
>
> I suppose I could check after each step (yuk):
>
> MyTask>>#go
>         ((self stepOne
>                 ifTrue: [self stepTwo])
>                         ifTrue: [self stepThree])
>         ...
>                         ifTrue: [self stepN]
>
> Or, I could check at the beginning of each step (*slightly* less yuk):
>
> MyTask>>#stepN
>         shouldRun ifFalse: [^ self].
>         ...
>
> Or:
>
> MyTask>>#stepN
>         self stepNMinusOne ifFalse: [^ self]
>         ...
>
> Or I could use a signal:
>
> MyTask>>#stepN
>         (self call: MyComponent new) ifFalse: [MyCancellation signal]
>
> MyTask>>#go
>         [self stepOne.
>         self stepTwo.
>         ...
>         self stepThree]
>                 on: MyCancellation
>                 do: [self answer: false]
>
> To me the signal looks the best, but feels like an abuse of the
> exception handling framework.
>
> What have others done? It's very possible I still don't know how to use
> tasks properly, so please feel free to correct me!

I agree (on everything else) and no, this doesn't have to do with your
task knowledge. Something you could use is announcements [1] to signal
cancelation right in your components (that you call in the steps) and
handle that only in one place so you don't have to check their return
value. As usual, pay attention to your subscribers to prevent "memory
leaks".

[1] http://onsmalltalk.com/programming/smalltalk/maintaining-loose-coupling-in-seaside-components/


Cheers
Philippe
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Task Escape

keith1y
In reply to this post by keith1y
Keith Hodges wrote:

> or... you could try a message eating null. I know that people are alway
> suspicious of letting message eating null loose in their code, however I
> do think that in a specific controlled situation it can be very useful.
>
> You example becomes...
>
> self stepOne stepTwo stepThree.
>
> if any of the steps returns Null then the subsequent steps are passed over.
>
> Null is available in universes.
>
> cheers
>
> Keith
>  
And you can explicitly test whether anyone cancelled.

self stepOne stepTwo stepThree ifNull: [ "they cancelled" ]

Keith
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Task Escape

Zulq Alam-2
In reply to this post by Philippe Marschall
Thanks Philippe. I have placed some comments inline.

Philippe Marschall wrote:

> 2008/1/9, Zulq Alam <[hidden email]>:
>>
>> MyTask>>#stepN
>>         (self call: MyComponent new)
>>                 ifFalse: [self answer: false]
>>
>> This seems to work OK but only if the task has been called. If it is the
>> root component, then the answer is a no-op.
>
> Right but, instead of #answer: ing you could as well #call: something like:
>
> MyTask>>#stepN
>         (self call: MyComponent new)
>                 ifFalse: [ self call: CanceledComponent new ]
>
> It's probably best to encapsulate such logic in an own method:
>
> MyTask>>#done
>         self call: CanceledComponent new
>
> MyTask>>#stepN
>         (self call: MyComponent new)
>                 ifFalse: [ self done ]
>

Ahh... interesting. Wouldn't you worry about the dirty task buried in
the call chain? What if something sends #home? Perhaps it's just not
worth worrying about...

> I agree (on everything else) and no, this doesn't have to do with your
> task knowledge. Something you could use is announcements [1] to signal
> cancelation right in your components (that you call in the steps) and
> handle that only in one place so you don't have to check their return
> value. As usual, pay attention to your subscribers to prevent "memory
> leaks".
>

They look interesting but I don't immediately understand how to do what
I want with them. I will have a closer look this weekend.

> [1] http://onsmalltalk.com/programming/smalltalk/maintaining-loose-coupling-in-seaside-components/

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Task Escape

Zulq Alam-2
In reply to this post by keith1y
Thanks Keith.

I'll be honest, the message eating null pattern gives me the fear. Just
the name sounds apocalyptic to me. :)

In this case, I suppose, it's like the state pattern where each step
returns the current state?

More explicitly this might be:

MyTask>>#cancel
     state := MessageEater new

MyTask>>#go
     state := self.
     state stepOne.
     state stepTwo.

MessageEater>>doesNotUnderstand: aMessage
     " Yum "
     ^ self

Presumably you could have additional states, although not sure what
these would be in this abstract example.

Thanks,
Zulq.

Keith Hodges wrote:

> or... you could try a message eating null. I know that people are alway
> suspicious of letting message eating null loose in their code, however I
> do think that in a specific controlled situation it can be very useful.
>
> You example becomes...
>
> self stepOne stepTwo stepThree.
>
> if any of the steps returns Null then the subsequent steps are passed over.
>
> Null is available in universes.
>
> cheers
>
> Keith

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Task Escape

Zulq Alam-2
In reply to this post by Zulq Alam-2
I have created a superclass for all my tasks, CancelAwareTask to work in
conjunction with a Notification subclass CancelNotification. In response
to a notification, the task will reset its state and then answer (if
possible).

CancelAwareTask>>#go
     " Subclasses should implement #run instead. "
     [self run]
         on: CancelNotification
         do: [self handleCancel]

CancelAwareTask>>#cancel
     CancelNotification signal

CancelAwareTask>>#handleCancel
     self reset.
     self answer

CancelAwareTask>>#reset
     " Reset any state. "
     self subclassResponsibility

CancelAwareTask>>#run
     " Implement instead of #go. "
     self subclassResponsibility


A run method might then be:

MyCancelAwareTask>>#run
     self stepOne
     self stepTwo
     ...
     self stepN

MyCancelAwareTask>>#stepN
     (self call: MyComponent new) ifFalse: [self cancel]

MyCancelAwareTask>>#reset
     iVarOne := nil.
     iVarTwo := nil.
     ...
     iVarN := nil

This seems to work and I like it because I don't have to change very
much to make it work. I suspect it's not very efficient though. Would be
interested in your comments.

Thanks,
Zulq.

Zulq Alam wrote:

> I have a task with N steps:
>
> MyTask>>#go
>     self stepOne.
>     self stepTwo.
>     ...
>     self stepN.
>
> Each step calls one or more components. Each component offer a Cancel
> link / button. If clicked, the component answers false. If a component
> answers false, the task should stop. I currently do this by checking the
> answer value and then answering if the value is false.
>
> MyTask>>#stepN
>     (self call: MyComponent new)
>         ifFalse: [self answer: false]
>
> This seems to work OK but only if the task has been called. If it is the
> root component, then the answer is a no-op.
>
> I don't intend for such tasks to be used as root components and
> practically this isn't a problem but it doesn't feel right. The task
> should work no matter how it is being used.
>
> I suppose I could check after each step (yuk):
>
> MyTask>>#go
>     ((self stepOne
>         ifTrue: [self stepTwo])
>             ifTrue: [self stepThree])
>     ...
>             ifTrue: [self stepN]
>
> Or, I could check at the beginning of each step (*slightly* less yuk):
>
> MyTask>>#stepN
>     shouldRun ifFalse: [^ self].
>     ...
>
> Or:
>
> MyTask>>#stepN
>     self stepNMinusOne ifFalse: [^ self]
>     ...
>
> Or I could use a signal:
>
> MyTask>>#stepN
>     (self call: MyComponent new) ifFalse: [MyCancellation signal]
>
> MyTask>>#go
>     [self stepOne.
>     self stepTwo.
>     ...
>     self stepThree]
>         on: MyCancellation
>         do: [self answer: false]
>
> To me the signal looks the best, but feels like an abuse of the
> exception handling framework.
>
> What have others done? It's very possible I still don't know how to use
> tasks properly, so please feel free to correct me!
>
> Thanks,
> Zulq.

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Task Escape

keith1y
In reply to this post by Zulq Alam-2
Zulq Alam wrote:
> Thanks Keith.
>
> I'll be honest, the message eating null pattern gives me the fear.
> Just the name sounds apocalyptic to me. :)
Its just an alternative form of nil, nothing to be afraid of.

See http://www.squeaksource.com/Null

The class documentation is extensive

Keith
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Task Escape

Philippe Marschall
In reply to this post by Zulq Alam-2
2008/1/10, Zulq Alam <[hidden email]>:

> I have created a superclass for all my tasks, CancelAwareTask to work in
> conjunction with a Notification subclass CancelNotification. In response
> to a notification, the task will reset its state and then answer (if
> possible).
>
> CancelAwareTask>>#go
>      " Subclasses should implement #run instead. "
>      [self run]
>          on: CancelNotification
>          do: [self handleCancel]
>
> CancelAwareTask>>#cancel
>      CancelNotification signal
>
> CancelAwareTask>>#handleCancel
>      self reset.
>      self answer
>
> CancelAwareTask>>#reset
>      " Reset any state. "
>      self subclassResponsibility
>
> CancelAwareTask>>#run
>      " Implement instead of #go. "
>      self subclassResponsibility
>
>
> A run method might then be:
>
> MyCancelAwareTask>>#run
>      self stepOne
>      self stepTwo
>      ...
>      self stepN
>
> MyCancelAwareTask>>#stepN
>      (self call: MyComponent new) ifFalse: [self cancel]
>
> MyCancelAwareTask>>#reset
>      iVarOne := nil.
>      iVarTwo := nil.
>      ...
>      iVarN := nil
>
> This seems to work and I like it because I don't have to change very
> much to make it work. I suspect it's not very efficient though.

Given all the overhead like sending stuff over the internet this
shouldn't be noticable.

Cheers
Philippe

> Would be
> interested in your comments.
>
> Thanks,
> Zulq.
>
> Zulq Alam wrote:
> > I have a task with N steps:
> >
> > MyTask>>#go
> >     self stepOne.
> >     self stepTwo.
> >     ...
> >     self stepN.
> >
> > Each step calls one or more components. Each component offer a Cancel
> > link / button. If clicked, the component answers false. If a component
> > answers false, the task should stop. I currently do this by checking the
> > answer value and then answering if the value is false.
> >
> > MyTask>>#stepN
> >     (self call: MyComponent new)
> >         ifFalse: [self answer: false]
> >
> > This seems to work OK but only if the task has been called. If it is the
> > root component, then the answer is a no-op.
> >
> > I don't intend for such tasks to be used as root components and
> > practically this isn't a problem but it doesn't feel right. The task
> > should work no matter how it is being used.
> >
> > I suppose I could check after each step (yuk):
> >
> > MyTask>>#go
> >     ((self stepOne
> >         ifTrue: [self stepTwo])
> >             ifTrue: [self stepThree])
> >     ...
> >             ifTrue: [self stepN]
> >
> > Or, I could check at the beginning of each step (*slightly* less yuk):
> >
> > MyTask>>#stepN
> >     shouldRun ifFalse: [^ self].
> >     ...
> >
> > Or:
> >
> > MyTask>>#stepN
> >     self stepNMinusOne ifFalse: [^ self]
> >     ...
> >
> > Or I could use a signal:
> >
> > MyTask>>#stepN
> >     (self call: MyComponent new) ifFalse: [MyCancellation signal]
> >
> > MyTask>>#go
> >     [self stepOne.
> >     self stepTwo.
> >     ...
> >     self stepThree]
> >         on: MyCancellation
> >         do: [self answer: false]
> >
> > To me the signal looks the best, but feels like an abuse of the
> > exception handling framework.
> >
> > What have others done? It's very possible I still don't know how to use
> > tasks properly, so please feel free to correct me!
> >
> > Thanks,
> > Zulq.
>
> _______________________________________________
> seaside mailing list
> [hidden email]
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside