Up to Main Index                          Up to Journal for November, 2023

                   JOURNAL FOR SATURDAY 4TH NOVEMBER, 2023
______________________________________________________________________________

SUBJECT: The type operator is too “magical”? :(
   DATE: Sat  4 Nov 18:55:10 GMT 2023

November is here, along with the cold, wet, windy weather. Still, it will soon
be time for the holiday season for everybody…

I think I have nearly finished work on adding the ‘any’ type to Mere. Although
a few people have called me out for getting too “magical” again. This is due
to the type operator reporting the inner type of a variable of type any. For
example:


    a := any 1
    println type a // displays "int" and not "any"


The argument is that type should always return the type of the variable, in
this case “any”, and that the type any breaks that. I tend to agree. The
question then becomes, if you have a variable of type any how do you get to
the inner type? Normally you would use a conversion:


    a := any 1
    i := int a // conversion to int


In this case we know the inner type is an int. But what if you don’t know what
the inner type is? For this I’ve added a “not any” operator ‘!any’ that
reverses an any conversion:


    a :=  any 1
    i := !any a // 'i' is now of type int


The type operator can now be non-magical and return “any” for a variable of
type any, then the type of the result, the inner value, can be checked:


    a := any 1
    println type a       // displays "any"
    println type !any a  // displays "int"


Where might this be useful? Consider a function that should accept any array
or map as a parameter:


    > cat test.mr
    call fn1 []int 1 2 3
    call fn1 [int] 1 "one", 2 "two", 3 "three"
    call fn1 "Test!"

    fn1: func a:any
      A := !any a     // unwrap 'a' as 'A'
      t := type A     // get unwrapped type
      if t[0] != "["
        printf "fn1: a is %s not an array or map.\n" t
        return
      fi
      range k; v; A
        println k ": " v
      next
    endfunc

    > mere test.mr
    0: 1
    1: 2
    2: 3
    1: one
    2: two
    3: three
    fn1: a is string not an array or map.
    >


Another example requires revisiting an earlier post of mine[1], with some
heavy rewriting and a touch of regular expressions just for good measure:


    call add 1 2
    call add "a" "b"
    call add 1 "a"
    call add true false

    add: func A:any B:any
      a := !any A
      b := !any B
      if (Ta := type a) != type b
        println "Cannot add A and B, different types."
      elif Ta ~m `^(int|uint|float|string)$`
        println a + b
      else
        println "Cannot add A and B, not numbers or strings."
      fi
    endfunc


Here the function is trying to add together two numbers or strings. When run
it produces:


    3
    ab
    Cannot add A and B, different types.
    Cannot add A and B, not numbers or strings.


Exactly how I’d validate that code with the static checker I’m not sure…

Another small fix I’ve made is related to labels. Currently any labels are
displayed as a bare int value in traces, dumps and from the literal built-in.
This can be confusing, they look just like an int. The fix I’ve made displays
labels as ‘label(…)’. This is in-line with how ‘regexp(…)’ and ‘any(…)’ values
are displayed:


    dummy: println literal dummy // displays "label(3)"


And now it's on to the boring task of updating the documentation for the new
‘any’ type and ‘!any’ operator… *sigh*

--
Diddymus

  [1] The journal “Boxing and unboxing variables”: ../10/21.html


  Up to Main Index                          Up to Journal for November, 2023