ID:117565
 
Not Feasible
Applies to:DM Language
Status: Not Feasible

Implementing this feature is not possible now or in the foreseeable future
In C++, you can declare types local to functions, like so:

int some_func(int a, int b)
{
    class ExampleClass
    {
        int whatever(int a, int b)
        {
            return a + b;
        }
    };

    return ExampleClass().whatever(a, b);
}


This can be handy for defining, among other things, functors, which would be very nice as a more first-class citizen in DM. Syntax would look something like this:

proc/some_func(a, b)
/ExampleClass // Leading slash to disambiguate from labels
proc/whatever(a, b)
return a + b

var/ExampleClass/c = new /ExampleClass
return c.whatever(a, b)


The fully-qualified typepath of a local type definition would be the fully-qualified path of the procedure plus the local definition of the type - e.g., /ExampleClass above is really of type /proc/some_func/ExampleClass, and is distinguishable from some global type ExampleClass. Local types take precedence over regular types - or perhaps local types must always be fully specified, thus allowing no ambiguity.

Local types are visible outside the procedure they are defined in.

This kind of behaviour can be semi-implemented in DM by writing code like the following:

local_type/some_func/ExampleClass
proc/whatever(a, b)
return a + b

proc/some_func(a, b)
var/local_type/some_func/ExampleClass/c = new /local_type/some_func/ExampleClass
return c.whatever(a, b)


but this is much less elegant, and the local definition version slides into being lambda functionality akin to that in C++0x reasonably easily.
I actually think you can do this, or something like this (although trying to add procs doesn't work):
proc/cake()
/my_cake
var/my_cake/c = new()


I haven't played with the syntax much, but I'm sure you can do something with it, I'm just not sure how much.
I'm not sure what the last code snippet is supposed to do. The last line has an error.

There's quite a lot between where BYOND currently is and what you'd need to support this. I see no use for what you're describing, but some of the necessary intermediate parts could be useful.

The ability to refer to procs by name would be nice:

proc
proc1(a)
a()

proc2()
world << "Hello, World!"

proc3()
proc1(proc2)


This is already possible with the call proc, but having the syntax for it makes it much cleaner looking.

In the example c++ code you have the line:

return ExampleClass().whatever(a, b);


It'd be nice to be able to do things like that with DM, though you'd have to use the : operator (also with lists, ex: some_list[1]:x to reference the x var of the first element in the list).
Forum_account wrote:
I'm not sure what the last code snippet is supposed to do. The last line has an error.

Whoops, my mistake. Corrected. It's supposed to call the 'whatever' proc on the ExampleClass instance.

There's quite a lot between where BYOND currently is and what you'd need to support this. I see no use for what you're describing, but some of the necessary intermediate parts could be useful.

I know it's a bit of a stretch-feature, and it can kind of be written by using ordinary types with a naming scheme so they don't collide, but it is extremely useful for supporting a more functional paradigm. Consider a more useful example:

functor
proc/func()
CRASH("Call to abstract base 'functor' made")

append_foo
func(s)
return "foo[s]"

proc/map(list/l, functor/f)
for(var/i = 1, i <= l.len, i++)
l[i] = f.func(l[i])

proc/test_proc()
var/list/test_list = list("a", "test", "list")

map(test_list, new /functor/append_foo)
for(var/k in test_list) world << k


That's the workaroundy version. Here's the more local version that I'd like to work:

functor
proc/func()
CRASH("Call to abstract base 'functor' made")

proc/map(list/l, functor/f)
for(var/i = 1, i <= l.len, i++)
l[i] = f.func(l[i])

proc/test_proc()
/append_foo
proc/func(s)
return "foo[s]"

var/list/test_list = list("a", "test", "list")

map(test_list, new /append_foo)
for(var/k in test_list) world << k


Obviously in this toy-example it's not much of a difference, but real uses of this kind of programming are much nicer if the functor definition can be kept as local to use as possible.

Then future feature requests would look at things like parameter capture/closures. Although I suppose that is a separate feature request, this is kind of related.

The ability to refer to procs by name would be nice:
> proc
> proc1(a)
> a()
>
> proc2()
> world << "Hello, World!"
>
> proc3()
> proc1(proc2)
>

This is already possible with the call proc, but having the syntax for it makes it much cleaner looking.

Functors are another solution, although certainly not nearly as clean. Basically, functions as first-class citizens.

In the example c++ code you have the line:
> return ExampleClass().whatever(a, b);
>

It'd be nice to be able to do things like that with DM, though you'd have to use the : operator (also with lists, ex: some_list[1]:x to reference the x var of the first element in the list).

Chaining of the . and : operators would be nice, yes.
And the next question is: how would this make game development easier?

Functional programming is rarely useful for things other than toy examples. I doubt that this would be very useful to game developers, especially not to DM developers.
Personally I've always felt local struct definitions were one of the worst elements of C++. There is little function to them, and they complicate the code something terrible. It generally involves no more effort to write a proper struct, or a proper set of helper functions, than to write a local one and the result is always far clearer.

Regardless, I don't see how this would ever be workable in DM syntax, let alone something that could be easily inserted into the compiler or the interpreter.
My biggest use of local struct stuff has been closure workalikes when I don't have C++0x.

Maybe I should make a seperate feature request for closure/first-class function support. Although one probably already exists...

Functional programming is rarely useful for things other than toy examples. I doubt that this would be very useful to game developers, especially not to DM developers.

I disagree most strongly. While functional programming a-la LISP isn't easy and isn't readily understandable unless you speak LISP, the concept of a closure is incredibly useful, and thinking of common programming tasks as "I have a list, and I want to do this to each of the elements" is often a good way to do it. I've got a lot of code at work that's much clearer expressed as a closure than it would be expressed strictly procedurally, and it's also magically multithreaded by the map() equivalent in use.
I didn't say it's never useful, I said it's rarely useful. Considering how few requests there are for functional programming features in DM, it would be rarely useful.

the concept of a closure is incredibly useful, and thinking of common programming tasks as "I have a list, and I want to do this to each of the elements" is often a good way to do it.

Is the DM method really that complex?

// this:
for(var/v in my_list)
my_proc(v)

// compared to:
apply(my_proc, my_list)


If you want, you can write the apply proc in DM and pass it the path of the proc. If the compiler could figure out that "apply" means "/proc/apply" you could have a cleaner syntax for this. That part could be useful, but that's about it.