% A counter class declare class Counter attr val meth browse {Browse @val} end meth inc(Value) val := @val + Value end meth dec(Value) val := @val - Value end meth init(Value) val := Value end end declare C = {New Counter init(0)} {C browse} {C inc(1)} {C browse} % What happens on concurrent counter access? thread {C inc(1)} end thread {C dec(1)} end {C browse} % will it always be 1? % Ports abstraction (asynchronous FIFO channel) declare P S in {NewPort S P} {Browse S} % will it always display 1|2|_? thread {Send P 1} end thread {Send P 2} end % A simple active object declare P in local Xs in {NewPort Xs P} thread {ForAll Xs proc {$ X} {Browse X} end} end end % After eta-reduction: declare P in local Xs in {NewPort Xs P} thread {ForAll Xs Browse} end end {Send P foo(1)} thread {Send P bar(2)} end % Port implementation using cells % % A wrapper/unwrapper implementation using higher-order programming % declare NewWrapper in proc {NewWrapper ?Wrap ?Unwrap} Key={NewName} in fun {Wrap X} fun {$ K} if K==Key then X end end end fun {Unwrap C} {C Key} end end declare NewPort Send in local Wrap Unwrap in {NewWrapper Wrap Unwrap} proc {NewPort S P} C={NewCell S} in P={Wrap C} end proc {Send P X} C={Unwrap P} Old in {Exchange C X|Old Old} end end declare P S in {NewPort S P} {Browse S} {Send P 1} thread {Send P 2} end % % defining active objects as objects with an internal thread % receiving messages on a port asynchronously and executing % them in a serialized manner % % An active counter object declare ActObj in local Obj Xs P in Obj={New Counter init(0)} {NewPort Xs P} thread {ForAll Xs proc {$ M} {Obj M} end} end proc {ActObj M} {Send P M} end end {ActObj inc(1)} % asynchronous send {ActObj browse} % What happens on concurrent counter access? thread {ActObj inc(1)} end thread {ActObj dec(1)} end {ActObj browse} % will it always be 1? % A NewActive function to create active objects from passive classes declare NewActive in fun {NewActive Class Init} Obj Xs P in Obj={New Class Init} {NewPort Xs P} thread {ForAll Xs Obj} end % after eta-reduction proc {$ M} {Send P M} end end declare ActiveCounter in ActiveCounter = {NewActive Counter init(0)} {ActiveCounter inc(1)} {ActiveCounter browse} thread {ActiveCounter inc(1)} end thread {ActiveCounter dec(1)} end {ActiveCounter browse} % will it always be 1? % % making the message sending synchronous % declare fun {NewSynchronousActive Class Init} Obj Xs P in Obj={New Class Init} {NewPort Xs P} thread {ForAll Xs proc {$ msg(M X)} {Obj M} X=unit end} end proc {$ M} X in {Send P msg(M X)} {Wait X} end end declare SyncActiveCounter in SyncActiveCounter = {NewSynchronousActive Counter init(0)} {SyncActiveCounter inc(1)} {SyncActiveCounter browse} thread {SyncActiveCounter inc(1)} end thread {SyncActiveCounter dec(1)} end {SyncActiveCounter browse} % will it always be 1? % % A bouncing ball example % declare class Bounce attr other count:0 meth init(Other) other:=Other end meth ball count:=@count+1 {@other ball} end meth get(X) X=@count end end declare B1 B2 in B1={NewActive Bounce init(B2)} B2={NewActive Bounce init(B1)} % Get the ball bouncing {B1 ball} % Follow the bounces {Browse {B1 get($)}} % Will the bouncing ball work with synchronous messages? % % An event manager design pattern from Erlang % declare class EventManager attr handlers meth init handlers:=nil end meth event(E) handlers:= {Map @handlers fun {$ Id#F#S} Id#F#{F E S} end} end meth add(F S Id) Id={NewName} handlers:=Id#F#S|@handlers end meth delete(DId DS) handlers:={List.partition @handlers fun {$ Id#F#S} DId==Id end [_#_#DS]} end end declare EM MemH Id in EM={NewActive EventManager init} MemH=fun {$ E Buf} E|Buf end {EM add(MemH nil Id)} {EM event(a1)} {EM event(a2)} declare Log in {EM delete(Id Log)} {Browse Log}