lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


[listproc choked on this one. Perhaps it was the MIME stuff.  -lhf]

 From: "John Belmonte" <jvb@prairienet.org>
 Subject: Simple implementation of function overloading
 Date: Wed, 19 Apr 2000 21:31:21 +0900

Here is a simple implementation of function overloading in Lua.  It's not
that I think Lua is in dire need of overloading, but instead I used this to
learn Lua programming and more specifically how to extend the language.
Also I'm interested in implementing a Dylan-like object/class system (which
itself is based on CLOS) which would require some function trickery, and
this is just a small stepping-stone toward that.

The nice thing about an overloaded function is that at calltime you know the
arguments are of the correct type, so no manual checking is required.  Even
if someone has little interest in overloading, this feature can be used for
simple type-checking of function arguments.

In my implementation if you want to overload a function you have to write
its name in a mangled form when you define it.  The mangling consists of
appending a double-underbar followed by the first letter of the built-in
type name of each argument.  (Except that '0' is used for nil, which I think
would rarely be used because overloading and nil arguments don't mix well).
For example:

    function add__nn( a, b )
        return a + b
    end

    function add__ss( a, b )
        return a..b
    end

Of course when you call the function you don't use the mangled name:

    print( add(5,6) )
    --> 11

    print( add("5","6") )
    --> 56

Originally I didn't plan to attach the source to this email, but instead
only to upload it to the files section of lua-l on eGroups.  However as it's
necessary to do a short registration for access, I thought it may deter some
people.  Anyway the file is there also, and I will keep it current with any
bugfixes:

    http://www.egroups.com/files/lua-l/


-John Belmonte

------------------------------------------------------------------------------
--
-- overloaded.lua - Simple implementation of function overloading.
--
-- John Belmonte <jvb@prairienet.org>
--
--  LIMITATIONS:
--      names must be mangled manually for function definitions
--      only global functions supported
--      overloading by built-in type only
--
--  TODO:
--      a high-level description of usage and implementation
--      remove implementation globals (FuncRecTag, tm_global_in, etc.)
--
--  ISSUES:
--      possible to accidentally overwrite generic function name
--          this can be caught with "setglobal" tag method for FuncRecTag
--
--  LOG:
--      2000-Apr-19     It's working.
--
------------------------------------------------------------------------------

$ifnot __child__
$debug
    __child__ = 1
    dofile "SafeGlobals.lua"
    dofile "DumpTable.lua"
$end

-- unmangle()
--
-- Returns the two components of a type-mangled function name such as:
--
--      "myfunc__sn"  - method myfunc() with args of type string and number
--
-- Returns nil if not a mangled name.  Note that is different from a
-- mangled name with no args, in which case the type part is the
-- empty string.
--
function unmangle( mangled_name )
    local closure = { table={} }
    local f = function (...)
        %closure.table = arg
    end
    gsub( mangled_name, "^(.+)__(.*)$", f )
    return closure.table[1], closure.table[2]
end

-- get_typestring()
--
-- Given a list of values, makes a string consisting of first letter
-- of the buit-in type name of each value.  The exception is nil, which
-- is represented as "0".  Example:
--
--      string, userdata, number  -->  "sun"
--
function get_typestring( list )
    local closure = { typestring = "" }
    local f = function( index, value )
        local typename = type(value)
        if (typename == "nil") then typename = "0" end
        %closure.typestring = %closure.typestring .. strsub(typename, 1, 1)
    end
    foreachi( list, f )
    return closure.typestring
end

-- remove any trailing nils from a typestring
function trimnils( typestring )
    return gsub( typestring, "^(.-)0*$", "%1" )
end

FuncRecTag = newtag()

-- When the value being assigned is a function and the name matches
-- our mangling format, unmangle the name and replace the given value
-- with our special function record.  The function record contains all
-- the variations of the overloaded name.  It also stores the overloaded
-- name itself for reporting purposes.
--
function tm_global_in( varname, oldval, newval )
    if type(newval) == "function" then
        local method_name, method_type = unmangle( varname )
        if (method_name) then
            local generic_func_rec = rawgetglobal( method_name ) or
                { name=method_name }
            generic_func_rec[trimnils(method_type)] = newval
            -- these are redundant after the first method
            settag( generic_func_rec, FuncRecTag )
            rawsetglobal( method_name, generic_func_rec )
            return
        end
    end

    -- TODO: chain original tagmethod
    rawsetglobal( varname, newval )
end

-- This tagmethod is triggered when a function call is attempted on
-- on one of our function records.  We then look up the specific function
-- based on the calling args type pattern.
--
function tm_func_out( generic_func_rec, ... )
    local typestring = get_typestring( arg )
    local method = generic_func_rec[trimnils(typestring)]
    if method then
        return call( method, arg )
    else
        error( "method '"..generic_func_rec.name..
            "' lookup failed for type '"..typestring.."'" )
    end
end

settagmethod( tag(nil), "setglobal", tm_global_in )
settagmethod( FuncRecTag, "function", tm_func_out )

------------------------------------------------------------------------------

$ifnot __child__
    function normalfunc()
        print( "hello..." )
    end

    -- a group of overloaded functios for foo()

    function foo__ns( num, string )
        print( "you made it" )
    end

    function foo__t( table )
        print( "a swell table" )
    end

    function foo__()
        print( "this one is void" )
    end

    function foo__f( f )
        f()
    end

    -- this will overwrite foo's overload handler
    --function foo() print( "whoops!" ) end

    normalfunc()
    foo( 55, "test" )
    foo( {} )
    foo()
    foo( nil, nil )         -- trailing nil args will be trimmed
    foo( function() print( "i'm functional" ) end )
    --foo( 25 )             -- error
    --foo( nil, "test" )    -- error
$end