brick-lang / brick

The Brick language spec
University of Illinois/NCSA Open Source License
31 stars 0 forks source link

Self-aware environments #3

Open weswigham opened 10 years ago

weswigham commented 10 years ago

Namely, a function should be aware of its context and be able to modify it.

Firstly, a function should have a method by which it can call itself, anonymous fn or otherwise. (it's certainly more clear if the method is the same)

fn -> 
    puts("Top level")
    (fn (i:Int) ->
         cond | i<10 ->
             puts("Inner level: #%d" % i)
             ^(i+1)
    )(0)

Which would output

Top level
Inner level: 0
Inner level: 1
Inner level: 2
Inner level: 3
Inner level: 4
Inner level: 5
Inner level: 6
Inner level: 7
Inner level: 8
Inner level: 9

Secondly, a function should probably have access to it's :before's and :after's; ie:

fn example ->
    cond | ^.before.length>0 -> 
                puts("Apparently something went before me... NO MORE.")
                ^.before.each -> |k, v|
                    ^.before[k] = nil
            | * -> 
                puts("I am alone in this world, as I should be.")

fn example:before ->
    puts("This happens before the example, bwahahaha")

This is to simply allow for dynamically adding and removing before and after hooks.

It would also be useful to have a function's type signature available to the function, and maybe even a copy of the arguments it was passed.

It would also be nice to extend the syntax (^^^^^'s!) to let a function get these same attributes of its caller (and therefore its caller's caller, and its caller's caller's caller, etc...)

zellio commented 10 years ago

Namely, a function should be aware of its context and be able to modify it.

Is it really a good idea to have mutable functions?

Firstly, a function should have a method by which it can call itself, anonymous fn or otherwise. (it's certainly more clear if the method is the same)

This I'm inclined to agree with. Though, arguably named functions are to be referenced by name / label. It would be very useful to either have a generic variable which is automatically populated with a reference to the closest closing function or some syntax to assign a local name as in clojure which handles this with an optional name argument in the anonymous function definition which is populated into the scope of the function, not above it.

(fn name? [params* ] exprs*)
(fn name? ([params* ] exprs*)+)

Secondly, a function should probably have access to it's :before's and :after's; ie: ... This is to simply allow for dynamically adding and removing before and after hooks.

This carries the same reservation as above. Why would we want mutability in our functions. Especially in the wrapping methods as they are generally used (in my understanding) to implement "mandatory functionality"

It would also be useful to have a function's type signature available to the function, and maybe even a copy of the arguments it was passed.

How is this beneficial? The types are essentially irrelevant at run-time as the compiler gurantees correctness.

It would also be nice to extend the syntax (^^^^^'s!) to let a function get these same attributes of its caller (and therefore its caller's caller, and its caller's caller's caller, etc...)

What is the use case for manually unrolling the stack? What benefit is there? You have access to values which are properly scoped.

weswigham commented 10 years ago

What is the use case for manually unrolling the stack? What benefit is there? You have access to values which are properly scoped.

The first and foremost benefit is being able to pass your parent anonymous function as a callback, situations arise where it would be convenient to do this in JS all that time. For example:

function () { //Outer anonymous function
    $.ajax("http://api.example.com?data=junk", {
        statusCode: {
            502: function() { //Then try again...
                //I want to call the outer anonymous function, what do I do? 
                //I could name it, but I don't think I should have to... what if the outer function is a callback itself?
            }
        }
    })
}

How is this beneficial? The types are essentially irrelevant at run-time as the compiler gurantees correctness.

Not usually at all to the function the signature belongs to; however, if I have a construct like HashMap<Hashable, Func<Object>>, it could certainly be reasonable for me to want be able to check the type of the function result and alter my execution based on it.

This carries the same reservation as above. Why would we want mutability in our functions. Especially in the wrapping methods as they are generally used (in my understanding) to implement "mandatory functionality"

Before/after hooks can be so much more than just "mandatory functionality". Let's suppose that I have an event driven library; I export functions named message_send and message_receive. Any other library in the application or top-level code may end up calling these functions. Let's now suppose that I'm adding a UI to my application here, and I want to make a sound on each message we receive. Since the syntax is available to do so, it would be convenient for me to simply go

fn message_receive:after 
    do_sound()

Now suppose that my message_receive library function gets a "bad" message, something that it wants to discard and pretend like it never happened; is there a way for it to stop the execution of its after-hooked functions? Is there a way to temporarily 'mute' the hooks? The mutable list isn't the only solution for sure, it just made sense from my dynamic programming background.

This I'm inclined to agree with. Though, arguably named functions are to be referenced by name / label. It would be very useful to either have a generic variable which is automatically populated with a reference to the closest closing function or some syntax to assign a local name as in clojure which handles this with an optional name argument in the anonymous function definition which is populated into the scope of the function, not above it.

I've considered this quite a bit... it would certainly be useful to have it be a language keyword that references the current function, it would probably more clear than introducing a sigil that does it, too. The reason I was clinging to the '^' was because then you could chain '^''s as a cool way to reference parent closures, BUT it would probably be more clear if it was instead something like keyword.parent.parent.parent .... The sigil is more concise, while a keyword is more clear.

That would bring us to the discussion of what the keyword could be... I'll immediately toss out this and self, since those holdovers from other languages don't quite convey that it's a closure you're accessing and not a plain object (plus, a method on an object would have both a concept of itself and the closure you're operating in).

Another reason I'd like to have a 'current closure' keyword is that since functions/blocks are first-class, it would make sense if that class had member functions, no? So without the keyword, how would you access those functions from within the function you're currently defining (assuming its anonymous)?

toroidal-code commented 10 years ago

I know you're reserved on using the this keyword, but I feel that it would do want we want, without the parsing ambiguity of the caret. self is used for the running object inside of a method. But this can refer to the running context.

I've always wanted Brick to be as verbose as possible, so I prefer the this.caller.caller style, as this explicitly states that the caller is a property of the context, and this simplifies reading.

If we decide that this carries too much weight from other languages (which it really does, but that's not insurmountable), what about our?

toroidal-code commented 10 years ago

Functions will have runtime type annotations associated with them, the way that all objects will have types to check at runtime, which is what permits pattern-matching. You can pattern match a function based on its' types.

weswigham commented 10 years ago

our is fairly good, I like it, anyways.

nickserv commented 10 years ago

How about something like this_function instead of our? I like the idea of using our instead of this to avoid overusing this, but I think meaning of our is too similar to this, which could get confusing. People might accidentally assume that they are just aliases to each other.