Thursday, November 26, 2009

Inline agents should be closures

Compare the current SmartEiffel inline agent definition:

local
   i: INTEGER
do
   agent (h: INTEGER) is
      do
         std_output.put_line(h.out)
      end (i)
      .call([])
end

with a real closure:

local
   i: INTEGER
do
   agent is
      do
         std_output.put_line(i.out)
      end
      .call([])
end

What do you think?

9 comments:

  1. Nice idea... I'm wondering how this would impact on implementation.
    As a side note I suspect that most non-die-hard fans of Eiffel will find the syntax "agent is do std_output.put_line(i.out) end.call([])" quite obscure, expecially for the dot-call alone in the line...
    BTW how should a closure like the following behave?
    foo is
    local i: INTEGER
    do
    i:=0
    10.times(do
    i:=i+1
    if i>8 then print("Ok%N") end
    end)
    print(i.out)
    end

    Shall it print:
    Ok
    Ok
    10
    or nothing because i is reset at the value it had at agent invocation (0)?

    Currently a class like this:

    class AGTE
    -- Agent test
    creation make

    feature
    make is
    local i: INTEGER
    do
    i:=5
    print("i="+i.out+"%N")
    10.times(agent is do i:=i+1 end)
    print("i="+i.out+"%N")
    end
    -- i: INTEGER
    end -- class AGTE

    crashes SmartEiffel, while
    class AGTE
    -- Agent test
    creation make
    feature
    make is
    do
    i:=5
    print("i="+i.out+"%N")
    10.times(agent is do i:=i+1 end)
    print("i="+i.out+"%N")
    end
    i: INTEGER
    end -- class AGTE
    outputs:

    i=5
    i=15


    I see that what we currently have are not "real" closures but I don't see the gain, at least explainable with a simplicistic example. Perhaps I'm missing something

    ReplyDelete
  2. Men, we miss an online Eiffel code formatter/colorer.... reading code like this is abysimal.

    ReplyDelete
  3. mmh. Without digging into theory now, I think it is good to have access to the variables defined in the lexical environment of the agent. First of all, because it improves readability.

    Thinking about inline agents, it may be a good idea to think about improving the .call([]). I'm definitely not a fan of introducing useless syntactic sugar, but an inline agent call just does not look nice. I have not settled an opinion here.

    Another question that arises when thinking about anonymous inner "functions" is whether anonymous inner classes are of any good. - I used them in Java often for the visitor pattern and here there are cool. Anyhow, I do not remember any other nice use cases.

    ReplyDelete
  4. Paolo, thanks for your remarks.

    Writeable entities are indeed something to think of but I think they should behave as you fell they should.

    I mean,

    i=5
    i=15

    SmartEiffel crashes because there is some test missing somewhere. In SmartEiffel i is out of the agent scope.

    I'll have to test other languages that have closures to know how they behave.

    Now, how do closures integrate with SCOOP :-)

    ReplyDelete
  5. closures in SCOOP? Is there anything special? - They are executes synchronously on the same processor as the calling code.

    ReplyDelete
  6. Closures mean access to the local variables. If they are not separate then they will be cloned as per Liberty's idea of SCOOP. Needless to say, i := i + 1 would not work as expected.

    ReplyDelete
  7. But the variables are only cloned, if the agent is executed on another processor, wouldn't it? I'd suggest to bind inline agents always to the same processor, as the "outlining" feature defining and using the inline agent. So by definition local variables would not need to be imported...

    ReplyDelete
  8. agent {MY_SEPARATE}.do_something

    Maybe this should be forgotten because there is a rule that a separate target must be an argument of the calling method... Well I don't know

    ReplyDelete
  9. Reading my last message... of course I meant "forbidden", not "forgotten"!!

    ReplyDelete