Hi Gisle,

you wrote:

>Elan's idea of being able to optionally specify return types of a
>function would be nice 

Thank you. I think this email contains a rather complete return type
enforcer function (further below).

>provided that all natives that can
>return more than one type used it.

This is not how I meant it. An optional return type specifier would be used
by the programmer who implements his own functions and wishes to prescribe
legal return types for his function. 

Here's an example for what I mean:

This works:

>> x f email@
connecting to: ...

This generates an error identifying the function f as the culprit:

>> x f ftp://
** Script Error: Expected one of: return type: [email!] - not: url!.
** Where: f ftp://

Let me explain (and demonstrate below):

The return type specifier is not intended to announce which types will be
returned. Rather, it is meant to exclude certain, unlisted, types from
being returned and - instead of returning them - it will alert you to the
fact your function is returning an unexpected type that you have outlawed
in your return type specification block. 

What's that good for? In a world in which functions have polymorphic return
values, you may be returning values from one function that are subsequently
handled by a chain of other functions *without* complaints - those
functions have polymorphic parameters - until some point in time in which a
function does not support that datatype. 

It is probably time consuming to detect where in your code that value
originates, since in our scenario the immediate suspect, the function that
evaluated the failed expression, is not to blame. 

Let me illustrate with a simple example. Given the functions f, and x:

f: func [value] [ return join value ["site.com"] ]

x: func [value] [ send value "Hi" ]

The following example works:
>> x f email@
connecting to: ...

The following example
>> x f ftp://

in REBOL/Core generates this error:

** Script Error: send expected address argument of type: email block.
** Where: send value "Hi

Since the error message quotes the send function as the source for the
complaint we would begin to search for the problem in function x.

Consider that in a real-word situation there may be any number of functions
that mediate between f and x, 

x .. w .. v .. u .. s .. t .. .. g .. f

and functions w .. g may have no problem dealing with url! types instead of
email! types. Here we would have to review function w thru g until we
arrive in f and determine that f is the source of improper datatype because
join is return type polymorphic.

Compare the previous error message to the following one:

>> x f ftp://
** Script Error: Expected one of: return type: [email!] - not: url!.
** Where: f ftp://

This error message tells you where the error originated - in f not in x -,
tells you it is a return type error, and what improper return type the
function attempted to return instead of using one of the legal choices
defined in the block.

Here is how the improved error message came about:

>> f: rt-func [[catch] value /local return-type [email!]] [ 
     return join value ["site.com"] 
   ]

>> x: func [value] [ send value "Hi" ]

>> x f ftp://
** Script Error: Expected one of: return type: [email!] - not: url!.
** Where: f ftp://

This is the version of rt-func I used:

rt-func: func [spec [block!] body [block!]] [
  insert body compose/deep [
    return: func [result /local return-type] [
      return-type: reduce [(spec/return-type)]
      either found? find return-type type? result [
        system/words/return result
      ][
        throw make error! reduce ['script 'expect-set 
          join "return type: " [mold return-type]
          mold type? result
        ]
      ]
    ]
  ]
  remove/part find spec 'return-type 2
  append spec [return]
  return func spec body
]

Hope this helps.

;- Elan >> [: - )]

Reply via email to