Up to Main Index                              Up to Journal for July, 2023

                     JOURNAL FOR SATURDAY 29TH JULY, 2023
______________________________________________________________________________

SUBJECT: Proper functions, shadowing and multiple assignments
   DATE: Sat 29 Jul 19:45:42 BST 2023

This week, and last week, real life has been pretty crap. I won’t bore people
with details. It’s been nice to get away from things and bury myself in Mere.

Most of this week has been taken up with making user defined functions act
like “proper” functions. What do I mean by proper functions? Well…

User defined functions can now have named parameters instead of using a string
map [string] called ‘@’:


    >cat message.mr
    call message "hello world!"
    exit

    message: func text
      println text
    endfun

    >mere message.mr
    hello world!
    >


Functions now support multiple return values. Here is a simple program to
return the minimum and maximum values in a list:


    >cat minMax.mr
    result = []int call minMax []int(7 1 6 9 8)
    println "min: " result[0] ", max: " result[1]

    result = []float call minMax []float(7.2 1.5 6.3 1.2 7.4)
    println "min: " result[0] ", max: " result[1]

    result = []string call minMax []string("q" "w" "e" "r" "t" "y")
    println "min: " result[0] ", max: " result[1]

    exit

    minMax: func list
      max = max = 0

      k = keys list
      kLoop:
        if list[k[0]] > max; max = list[k[0]]
        if list[k[0]] < min; min = list[k[0]]
      if len(k = delete k 0) > 0; goto kLoop
      return min max
    endfunc

    > mere minMax.mr
    min: 1, max: 9
    min: 1.2, max: 7.4
    min: e, max: y
    >


However, collecting the return values in an array is very clunky and ugly. It
also only works if all the values are of the same type. Is there a better way?
Mere now has a multiple assignment operator ‘><’. Using multiple assignment
with the above example we can now write:


    >cat minMax.mr
    min max >< call minMax []int(7 1 6 9 8)
    println "min: " min ", max: " max

    min max >< call minMax []float(7.2 1.5 6.3 1.2 7.4)
    println "min: " min ", max: " max

    min max >< call minMax []string("q" "w" "e" "r" "t" "y")
    println "min: " min ", max: " max

    exit

    minMax: func list
      max = max = 0

      k = keys list
      kLoop:
        if list[k[0]] > max; max = list[k[0]]
        if list[k[0]] < min; min = list[k[0]]
      if len(k = delete k 0) > 0; goto kLoop
      return min max
    endfunc

    > mere minMax.mr
    min: 1, max: 9
    min: 1.2, max: 7.4
    min: e, max: y
    >


As you might expect, multiple assignment can be used to swap variables, even
if they are of different types:


    >cat swap.mr
    a = 42
    b = "hello"
    println a " " b
    a b >< b a
    println a " " b

    >mere swap.mr
    42 hello
    hello 42
    >


The left hand side of a multiple assignment can be any variable, except an
array or map element. Here is an alternative way of writing a loop over a map:


    >cat loop.mr
    x = [string] "a" "ant", "b" "bat", "c" "cat"
    k = keys x
    kLoop:
      e k >< x[k[0]] delete k 0
      println e
      if len k > 0; goto kLoop

    >mere loop.mr
    ant
    bat
    cat
    >


Finally a ‘new’ token has been added to Mere which creates a new scope for a
variable. First an example that does not work as expected:


    >cat wrong.mr
    count = 42
    println "function: " call f []int(1 2 3)
    println "  global: " count
    exit

    f: func list
      count = len list
      return count
    endfunc

    >mere wrong.mr
    function: 3
      global: 3
    >


The problem here is that function ‘f’ is updating the global count instead of
a local instance. This can easily be fixed with the ‘new’ token:


    >cat fixed.mr
    count = 42
    println "function: " call f []int(1 2 3)
    println "  global: " count
    exit

    f: func list
      new count = len list
      return count
    endfunc

    >mere fixed.mr
    function: 3
      global: 42
    >


Why do I call ‘new’ a token? At the moment ‘new’ is neither an operator or a
built-in. It will probably be reserved as an operator before this version of
Mere is released.

Functions have their own scope and can be nested and shadow other functions in
outer scopes:


    >cat shadow.mr
    call display "a" // calls global display
    call shadow "b"
    exit

    shadow: func text
      call display text // calls local display
      return

      // Nested display function local to shadow
      display: func text
        println "shadow "+text
      endfunc
    endfunc

    display: func text
      println "global "+text
    endfunc

    >mere shadow.mr
    global a
    shadow b
    >


Here we have a global ‘display’ function. There is also another ‘display’
function nested in the ‘shadow’ function. However, the nested ‘display’ is
local to, and can only be accessed by[1], the ‘shadow’ function.

I’ve been slowly rewriting “The Cottage” with these improvements. It’s been an
ideal test-bed and thrown up quite a few issues along the way.

And that’s what I’ve been doing this week. What have you done? :)

--
Diddymus

  [1] Not strictly true as the shadowed ‘display’ function can be returned by
      ‘shadow’ and then called outside of it:

      >cat escape.mr
      call display "a"
      call ((call shadow "b") "c")
      exit

      shadow: func text
        call display text
        return display      // returns shadowed display

        display: func text
          println "shadow "+text
        endfunc
      endfunc

      display: func text
        println "global "+text
      endfunc

      >mere escape.mr
      global a
      shadow b
      shadow c
      >

      But that’s getting a little advanced, explaining it would be a good
      topic for a another whole post ;)


  Up to Main Index                              Up to Journal for July, 2023