signal - pannous/warp GitHub Wiki

Signals can be sent from any branch of any thread at any time using emit (or send without to); raise and throw are for errors only (P163). fire, trigger and signal work as emit, with a note. They can be received in multiple locations using the on keyword. Errors are not signals: a raised error reaches only a try higher up in the call stack, never an on handler.

fun check the surroundings {
  if man nearby: 
    emit stop the machine!
}

fun main {
 go check the surroundings // asynchronous
 on stop the machine{
  cancel everything
  panic
  exit
}
}

Because errors have their own value mechanism, using signals for control flow is encouraged.

By default a signals type is its name which need not be quoted. emit stop the machine => on stop the machine. An emit nobody handles does nothing; a raise is an error, which try catches, never an on handler. As everything in angle, Signals can be structured data as well, and metadata can be added effortlessly:

emit stop the machine{ time:now reason:"human in danger!"}

Signals can also be broadcast outside the application and runtime context, via the broadcast function and via well configured repeaters. Signals can also be received from outside the application and runtime context, via well configured channel listeners. This functionality is part of the standard library and not of the language abi (except for standardized serialization) and seemingless integration with the above on keyword, as well as when before after during once and whenever. (todo)

These Signal capturing keywords (after...) can also be used on variables to create (aspect oriented) implicit listeners:

x=10
once x==5 {print "countdown halfway done"}
while x-->0 : print x

Other than conditionals, listeners can react on access: set, get and change

# a while without a body 👍 (card while-without)
x=10
on set x {print "x changed to " value}
while x-->0

The signal event can be accessed within listeners with the signal/event keyword

These Signal capturing keywords can also be used on function names to create (aspect oriented) implicit listeners:

def test: print "test"
after tested: print "ok"
test()

Todo: shall event listeners be registrable post-hoc? that is should they semantically be pre inserted before the code by the compiler? The rational behind this is that events might be over before the listeners are properly registered

Variable listeners (warp)

once cond {…} runs its block the first time the condition holds after a write of a variable it reads; whenever cond {…} each time the condition becomes true (P156: an edge, like an interrupt line; a write while it already holds does not run it again). The checks follow each later write, loops included.

What to write for the other behaviours (each checked with warp eval):

you want write with x = 6; x = 7; x = 7; x = 3; x = 8
each time the condition becomes true whenever x > 5 {…} runs 2 times (6, 8)
at every change while it holds on change x { if x > 5 {…} } 3 times (6, 7, 8)
at every write while it holds on set x { if x > 5 {…} } 4 times (6, 7, 7, 8)
repeatedly, as long as it holds on every 1 second { if x > 5 {…} } (a timer), or a loop while x > 5 {…}
x = 0
n = 0
whenever x > 5 { n += 1 }
x = 6
x = 7
x = 3
x = 8
n
x = 1
once x == 5 { print "halfway" }
for i in 1..10 { x = i }
x

Derived signals and change listeners

A := value (charged) is a derived signal: a listener on it watches the variables its definition reads. on change x {…} runs after a write that changed the value of x (a variable or a := value); value is the new value.

price = 3
count = 2
total := price * count
whenever total > 10 { print "big order: " total }
on change total { print "total is now " value }
count = 5

Signals in object fields

A listener on a field of a parameter listens to that field of the object it is given, and a variable kept in a field stays a signal there (samples/signal_fields.warp):

watch(p) := on change p.age { print "age now " + value }
birthday = 30
person = {name: "Alice", age: birthday}
watch(person)
birthday = 31        // age now 31
person.age = 32      // age now 32