elvish icon indicating copy to clipboard operation
elvish copied to clipboard

Allow introspecting builtin functions

Open zzamboni opened this issue 7 years ago • 11 comments

For consistency, I think it would be nice if all functions (including builtins) allowed introspection, at least for the arguments, and respond to has-key so that other elements can be checked for existence.

[~]─> fn my-echo [@arg]{ echo $@arg }
[~]─> keys $my-echo~
▶ arg-names
▶ rest-arg
▶ opt-names
▶ opt-defaults
▶ body
▶ def
▶ src
[~]─> keys $echo~
Exception: fn cannot have its keys iterated
[tty], line 1: keys $echo~
[~]─> put $my-echo~[rest-arg]
▶ arg
[~]─> put $echo~[rest-arg]
Exception: not indexable
[tty], line 1: my $echo~[rest-arg]
[~]─> has-key $my-echo~ arg-names
▶ $true
[~]─> has-key $echo~ arg-names
Exception: couldn't get key or index of type 'fn'
[tty], line 1: has-key $echo~ arg-names

zzamboni avatar May 21 '18 20:05 zzamboni

In a somewhat similar fashion, it would be great to have a function like has-internal or has-builtin, or some other way to check if a function exists locally. I've tried to write elvish code that detects if read-upto exists in an Elvish release, and I couldn't find a good way to do that.

href avatar Jan 08 '20 01:01 href

a function like has-internal or has-builtin, or some other way to check if a function exists locally

A builtin that simply lists all builtins should be the core addition. Given that builtin it is trivial to write has-builtin as an elvish function. Similarly a builtin that also lists the known elvish functions is similarly valuable.

krader1961 avatar Jan 08 '20 02:01 krader1961

you can list all builtins with use builtin; keys $builtin:. has-builtin is simply has-key $builtin: echo~

SolitudeSF avatar Jan 08 '20 07:01 SolitudeSF

@SolitudeSF thanks!

@href I have improved https://github.com/href/elvish-gitstatus/pull/7 using this tip 😄

zzamboni avatar Jan 08 '20 07:01 zzamboni

How should math:, str:, and similar namespaces be handled per https://www.elvish.io/ref/language.html#pre-defined-modules? At the moment doing use str always uses the builtin module even if there is a ~/.elvish/lib/str.elv file. Is that guaranteed? Should it even be necessary to use re or use str given that you don't have to use builtin? Why do I even have to use builtin to be able to do keys $builtin: given that I can use all the builtins without the use builtin statement?

krader1961 avatar Apr 24 '20 04:04 krader1961

@krader1961 these are good questions. I agree it might make sense to automatically load all builtin modules, since they are commonly used anyway. The only counterpoint would be if doing so has any performance/size impact (@xiaq?). Since Elvish is a single binary, I would expect this not to be the case.

zzamboni avatar Apr 27 '20 18:04 zzamboni

@zzamboni There should be zero performance or size impact since, as you noted, elvish is by default a statically linked binary. Even if elvish wasn't a statically linked binary, doing an implicit use str (or any other builtin module), should be so close to zero cost that it is a rounding error in measuring something like that.

@xiaq, I am still perplexed by the current situation that resulted in the questions in my previous comment. Notwithstanding a few nights to cogitate on the situation. This appears to be solely due to the evolution of the project without anyone noting the inconsistency of how the builtin: namespace is handled versus newer core namespaces.

krader1961 avatar Apr 28 '20 04:04 krader1961

@krader1961 there are two reasons:

  • Consistency in form: all modules must be imported before use.

    Granted, the builtin namespace is special, but its specialty goes beyond just being "pre-imported"; its symbols can be referenced in unqualified form. To bring other modules onto equal footing with builtin namespace means making their symbols available in unqualified form as well - this is how import works in some languages, but I consider those languages backwards. So there is no sense in pursuing parity between the builtin module and other core modules.

    Also, the fact that you can do use builtin to import the builtin namespace is mostly for convenience of introspection. The builtin namespace is not a normal namespace.

  • Another smaller issue is that not actually all the modules are free to import. There are also modules written in Elvish, like epm, that does incur cost when imported. To pre-import epm would add cost; to pre-import only modules implemented in Go but not those implemented in Elvish is inconsistent and exposes an implementation detail unnecessarily.

That said, I can see a point in making it frictionless to use such essential modules as str:, but so far I am not leaning enough into that direction. This might change in future.

xiaq avatar Apr 28 '20 21:04 xiaq

@xiaq, Fair enough. Explaining the inconsistency between automatically using builtin modules like str versus external modules is perhaps the most salient point. While epm is bundled into the binary it is effectively an external module written in Elvish script. Having to explain why someone has to use epm but not use str is definitely problematic. I don't have a good answer for how to square that circle other than that it seems to me some modules are "more equal" than others; i.e., they are closer in spirit to builtin than epm.

krader1961 avatar Apr 28 '20 22:04 krader1961

See also issue #1171.

krader1961 avatar Oct 20 '20 04:10 krader1961

I see that issue #1123, which I opened after this one, is identical so I'll close my issue as a duplicate. What prompted me to open the same issue was the desire to determine if the search-external builtin supported a specific option (e.g., &all). I, obviously, agree with @zzamboni that functions written in Go should have parity with Elvish functions vis-a-vis introspection. Obviously with some possible caveats such as indices like $func~[body].

krader1961 avatar Oct 26 '20 04:10 krader1961