Handlers and Functions in WebTalk
==================================

Right, so this is something that trips people up quite a bit, especially if you're coming from other languages where everything is just a "function". In WebTalk (and LiveCode, HyperCard, etc.), there's a real distinction
between handlers and functions -- and it matters.

Hopefully, this is not as clear as mud by the end of it.


What's a Handler?
-----------------

A handler is a block of code that DOES something. It performs actions. It moves things about, changes properties, puts text into fields, plays sounds -- whatever. The point is: it applies to other things in the stack.

You define a handler with "on" and close it with "end":

    on resetInterface
      put "" into field "output"
      set the textSize of field "output" to 12
      hide button "cancel"
    end resetInterface

A handler doesn't return a value necessarily. It just does its job and gets on with it. Think of it like telling someone: "Go and tidy the kitchen." You don't expect them to hand you something back -- you just expect the kitchen to be tidy when they're done.

The most common handlers you'll write are message handlers -- things like mouseUp, mouseDown, openCard. These respond to events:

    on mouseUp
      put "You clicked me!" into field "status"
    end mouseUp

But you can also write your own custom handlers for any task you find yourself repeating in multiple places.


What's a Function?
------------------

A function is a block of code that CALCULATES something and gives you back a result. It takes some input, does some work, and returns a value.

You define a function with "function" and close it with "end":

    function celsiusToFahrenheit pCelsius
      put (pCelsius * 9 / 5) + 32 into tResult
      return tResult
    end celsiusToFahrenheit

The critical difference: a function MUST contain a return statement. In WebTalk, this is strictly enforced -- no ifs, no buts. If you write
a function without a return statement, you'll get an error:

    "No return statement specified in function 'celsiusToFahrenheit'"

This isn't just being fussy for the sake of it. It's the whole point of a function -- if it doesn't return something, it should probably be scripted as a handler instead. (More on why this is enforced further down).


The Key Difference (in plain terms)
------------------------------------

- A handler DOES things (side effects).
- A function EVALUATES things (returns a result).

A handler is like saying: "Paint the wall blue."
A function is like asking: "What colour would I get if I mixed red and blue?"

Handlers change something. Functions answer a question.

Sending Parameters to Handlers
------------------------------

You can send parameters to handlers in two ways.

With parentheses:

    resetFields("output", "status")

Without parentheses (space-separated):

    resetFields "output", "status"

Both call the same handler:

    on resetFields pField1, pField2
      put "" into field pField1
      put "" into field pField2
    end resetFields

The parameters are received as named variables (pField1, pField2) that only exist for the duration of that handler. Once the handler finishes, those variables are gone. They aren't global (as other variables are in webtalk by default)

You can also call handlers with no parameters at all:

    resetInterface


Sending Parameters to Functions
-------------------------------

Functions are always called using parenthesest:

    put celsiusToFahrenheit(100) into tTemp

You can pass multiple parameters, separated by commas:

    function distance pPoint1, pPoint2
      put item 1 of pPoint1 - item 1 of pPoint2 into dx
      put item 2 of pPoint1 - item 2 of pPoint2 into dy
      put sqrt(dx * dx + dy * dy) into tDist
      return tDist
    end distance

And call it like:

    put distance("100,200", "300,400") into tDist

Because functions return a value, you can use them inline -- anywhere you'd normally use an expression:

    put "Temperature: " & celsiusToFahrenheit(100) & " F"

    if distance(point1, point2) > 100 then
      put "Too far away"
    end if

You can even nest them:

    put round(celsiusToFahrenheit(bodyTemp))

Try doing that with a handler. (You can't -- handlers don't give you anything back to work with inline).


Using Callbacks in Handlers
---------------------------

Callbacks are a pattern where you essentially tell a handler: "When you've finished, give this other handler a shout with the result."
This is particularly useful for asynchronous operations -- things that take time, like fetching data from a server.

WebTalk's runScript command supports callbacks:

    runScript("arrayExchange.lc?action=ping", "", "onPingComplete")

The third parameter is the callback handler name. When the script finishes, WebTalk goes looking for a handler called "onPingComplete" and sends it the result. It follows the message path -- checking the current object first, then the card, then the stack:

    on onPingComplete pData
      put "Server says: " & pData into field "status"
    end onPingComplete

This is the handler pattern at its best. The callback DOES something with the data (puts it into a field). It doesn't need to return
anything -- it just cracks on with the job.

You can use the same pattern with postScript:

    postScript("arrayExchange.lc?action=process", myArray, "onProcessed")

    on onProcessed pResult
      put "Processing complete"
      put pResult into field "results"
    end onProcessed

The callback approach keeps your code tidy. Instead of hanging about waiting for a response, you fire off the request and get on with
things. When the answer comes back, your callback handler deals with it.


Using "return" in Functions (and Why It's Required)
---------------------------------------------------

Here's where WebTalk gets a bit opinionated - based on my whims, (and I think rightly so).

In LiveCode, you can technically use "return" in a handler, and the calling code can then check "the result" to get the value. This muddies
the waters between handlers and functions, and it leads to messy code.

Geoff Canyon put it rather well: "There is usually no reason to make a handler pretend to be a function."
https://livecode.byu.edu/articles/g_canyon_functions.html

In WebTalk, the rule is simple:

    If your code needs to return a value, make it a function.
    If your code needs to do things, make it a handler.

When you define something with "function", WebTalk checks that it contains a return statement. If it doesn't, you get an error at definition time -- not at runtime when something mysteriously doesn't work. Better to catch it early than spend ages scratching your head.

Here's a function that returns a value:

    function initials pFirstName, pLastName
      put char 1 of pFirstName & "." & char 1 of pLastName & "." into tInit
      return tInit
    end initials

And you use it like:

    put initials("Andy", "Smith") into field "initials"
    -- puts "A.S." into the field

The return value flows directly into wherever you called the function. That's the handy bit -- the function is a self-contained calculation
that you can use to slot in anywhere.

If you tried to write that as a handler instead:

    on initials pFirstName, pLastName
      put char 1 of pFirstName & "." & char 1 of pLastName & "." into tInit
      -- now what? Where does tInit go?
    end initials

You'd have to shove the result into a global variable or a field, which ties the handler to a specific context. The function version
doesn't care where its result ends up -- that's the business of the line that called it (back in your button, for example).


When to Use Functions/Handlers
-----------------

A good rule of thumb, use a HANDLER when:
- You're responding to an event (mouseUp, openCard, etc.)
- You're performing actions (moving objects, changing properties)
- You're doing something situation-specific in your stack (that you want to reuse on multiple objects)
- You need callbacks for async operations

Use a FUNCTION when:
- You're calculating or trying to ascertain a value
- You need to reuse the same logic to return a result
- The result could be used inline in an expression
- You're processing data (like a specific property of an object)

Something else worth mentioning: if you find yourself writing a handler that puts its result into a specific variable or field, have a think about whether it should be a function instead. Functions are more flexible sometimes because the calling code decides what to do with the result.


Quick Reference
---------------

Handler:
    on myHandler pParam1, pParam2
      -- do things
    end myHandler

    Call: myHandler "value1", "value2"
    Call: myHandler("value1", "value2")

Function:
    function myFunction pParam1, pParam2
      -- calculate things
      return tResult -- <! REQUIRED
    end myFunction

    Call: put myFunction("value1", "value2") into tVar
    Call: if myFunction(x) > 10 then ...


And that's about the size of it. Handlers do things. Functions calculate things. WebTalk enforces the distinction so your code stays tidy and your intentions to anyone following your script stay clear (hopefully). It might feel a bit strict at first, but the logic is that scripts are easier to read, easier to debug, and easier to reuse.

Hopefully that all makes sense.
