Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Consider adding callinfo for introspecting method calls #189

Open
faultyserver opened this issue Apr 21, 2018 · 6 comments
Open

[RFC] Consider adding callinfo for introspecting method calls #189

faultyserver opened this issue Apr 21, 2018 · 6 comments
Labels
feature-request Any request for a new feature of the language. Includes both syntax and library features. syntax Any issue relating to the syntax of Myst.

Comments

@faultyserver
Copy link
Member

callinfo would be a language-managed variable, similar to self, that provides some introspective information about the caller of a method. As a language variable, callinfo would also be added as a new keyword.

Having access to meta information about the caller of a method is primarily helpful for debugging and tooling inside of Myst. The most obvious usecases are the Spec and Assert libraries.

Most spec libraries provide location information about failing specs to help users locate them quickly and easily. In Myst, this isn't a practical possibility yet, because the library has no way of accessing that location information without requiring the user to pass in __FILE__ and __LINE__ values for every it block.

Adding callinfo would provide that location information to these modules automatically, without any burden on the user. It would also avoid having conditional semantics for the magic constants, which I've never particularly liked.

Semantics

Referencing callinfo outside of a method definition will always return nil.

Inside of a method definition, callinfo returns a Map with these entries:

  • :file: the file containing the call to this method.
  • :line: the line number of the call to this method.
  • :dir: the directory containing the file with the call to this method.

For simplicity, this Map is newly-created every time callinfo is invoked. This ensures both "psuedo-immutability", and that the values are accurate for the current call.

Example usage

Retrieving the location information of a Call using callinfo could look something like this:

def foo(a, b, c)
  {line: line, file: file} = callinfo
  STDOUT.puts("line <(line)>, file <(file)>")
end

foo(1, 2, 3) #=> line 6, file /Users/.../foo.mt
@faultyserver faultyserver added syntax Any issue relating to the syntax of Myst. feature-request Any request for a new feature of the language. Includes both syntax and library features. labels Apr 21, 2018
@Jens0512
Copy link
Member

In Crystal, you can define methods using things like this, like this:

put_line
def put_line(line=__LINE__)
    puts "Called from line #{line}"
end

Which outputs: Called from line 1 (https://play.crystal-lang.org/#/r/3xak).
Is this not possible in Myst? If it is not, making it so, seems sufficient, and much more effective, to me.

I mean, this callinfo proposed is just a helper for defining methods like in the example, if this callinfo needs to be set for every call (which it has if I am not mistaken), I think we should be very careful.
Take a recursive method like factorial, if the method calls itself a thousand times, wouldn't the stack have a thousand and one callinfos? factorial doesn't even need any callinfo.

I have by no means any proper proof that this will have a practical effect on performance, but I think that we must be careful about this kind of thing, and that there is no need for calls to contain this kind of callinfo without it being explicitly stated like in the examlpe.

@Jens0512
Copy link
Member

Jens0512 commented Apr 23, 2018

Maybe some sort of tag however can make this callinfo avaible, like

@[WithCallInfo]
def put_line
    STDOUT.puts(callinfo[:line])
end

(This example was just the first thing that popped into mind, and I have not thought properly about it at all. And I know we have no such attributes currently)
EDIT: Given some thought; bad idea XD

@faultyserver
Copy link
Member Author

I would really rather avoid re-using __LINE__ and __FILE__, since it puts a condition on what they mean depending on the context (i.e., it becomes "__LINE__ is replaced by the line number that the token appears on, except when it's in a method definition, where it means the caller's line number"). I don't like that overload, and it would be the only overload like that in the entire language.

As for how it gets set, it should be possible to calculate the value on the fly, without any need for a stack. Even if there was, it would probably be part of the callstack, which already tracks every Call that gets made, so the overhead would be pretty minimal.

@Jens0512
Copy link
Member

__LINE__ is replaced by the line number that the token appears on, except when it's in a method definition, where it means the caller's line number.

I have always seen it as: the token appears on the line number its called from, even though not passed, as it is the default parameter, like this:

foo (line:__LINE__) # Line 1
        # ^^^^^^^^ Token appears here
foo#(line:__LINE__)   Line 3
        # ^^^^^^^^ And here, at least that is how I have understood default parameter values

def foo(line=__LINE__); ... ; end

Personally at least, I think having it like that is pretty rational.

But, I agree now, seing how every Node already has their #location; not much reason not to have this callinfo keyword.

@faultyserver
Copy link
Member Author

Huh, I hadn't seen that description of it before. That's certainly a little bit nicer than what I wrote. I'm still not a huge fan of those semantics, either, cause they are still a special case. For example, if you reference an instance variable as a default value, it still gets evaluated in the context where the method is defined, while __LINE__ is where the method is called.

There's definitely a need for discussion about what default arguments should look like, and maybe there's some nicer solution that will come with that, but for now I'd like to avoid overloading the constant (visually and/or implementation wise).

I'd be down to change callinfo into something like __CALLER__ to keep the look/feel of the magic constant, though.

@Jens0512
Copy link
Member

Oh god, please not __CALLER__ 😄 it’d look absolutely terrible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request Any request for a new feature of the language. Includes both syntax and library features. syntax Any issue relating to the syntax of Myst.
Projects
None yet
Development

No branches or pull requests

2 participants