JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.71k stars 5.49k forks source link

Parser error when trying to add module scope to an export #14472

Open timholy opened 8 years ago

timholy commented 8 years ago

(Note: clearer statement and example in https://github.com/JuliaLang/julia/issues/14472#issuecomment-331151751)

I'd like it if using A could export a symbol from module B, using the module name as a qualifier:

export B.save

This has come up as an issue in the interaction between JLD and FileIO. FileIO exports load and save; after long discussion (CC @SimonDanisch), we decided that modules using FileIO should not extend these methods, but that FileIO should dispatch to unexported functions of the same name: in other words, FileIO should call JLD.save, a function which is not exported by JLD, even though JLD has using FileIO in its definition.

The problems come when a module like JLD wants to (perhaps for backwards-compatibility reasons) export FileIO's save rather than its own save.

Simple example:

module MyFormat

using FileIO

# Here's the line I'd like to uncomment:
# export FileIO.save

# In-line FileIO registration (for testing purposes)
add_format(format"MYFMT", "MYFMT", ".myfmt", [:MyFormat])

function save(f::File{format"MYFMT"}, x)
    open(filename(f), "w") do io
        write(io, magic(format"MYFMT"))
        print(io, '\n')
        print(io, "x = ", x)
    end
end

end

But if I uncomment that export line, I get

julia> using MyFormat
ERROR: LoadError: syntax: extra token "." after end of expression
 in include at ./boot.jl:261
 in include_from_node1 at ./loading.jl:392
 in eval at ./boot.jl:264
 [inlined code] from ./sysimg.jl:14
 in require at ./loading.jl:332
 in eval at ./boot.jl:264
while loading /tmp/MyFormat.jl, in expression starting on line 6

Obviously we could solve this by calling the internal method _save, but at this point it would be nicer not to have to change our expectations of packages that use FileIO.

Since this looks like a parser error, I'm wondering whether this could be fixed by changing the parser? Or would this introduce some other kind of problem?

Mildly related: #1986.

JeffBezanson commented 8 years ago

In this example, does MyFormat.save extend FileIO.save? If not, I don't think this will be possible. When you get name x from M via using, you have to get M.x.

Otherwise, I suppose this is equivalent to import FileIO.save; export save?

ssfrr commented 8 years ago

@timholy did the discussion on whether to extend FileIO.save happen in an issue I can look at? It's tripped me up in the past and I'm curious, but don't want to derail this thread too badly. :)

timholy commented 8 years ago

In this example, does MyFormat.save extend FileIO.save?

No, it doesn't. Instead, FileIO.save calls MyFormat.save with explicit scoping.

@timholy did the discussion on whether to extend FileIO.save happen in an issue I can look at?

You can start reading here: https://github.com/JuliaIO/FileIO.jl/pull/43#discussion_r41086910

timholy commented 8 years ago

When you get name x from M via using, you have to get M.x.

I can change that using to import, if it changes anything.

bicycle1885 commented 8 years ago

Is there any progress here? I also want the export syntax like export FileIO.save. In my case, I want to write like:

module Seq

module RE
type Regex
    # ...
end

function matched(re::RegexMatch)
    # ...
end

macro prosite_str(pat)
    $(Regex(pat))
end
end # module RE

# this syntax is not supported!
export .RE.Regex, .RE.matched, .RE.@prosite_str

end # module Seq

Or more compactly:

# this syntax is not supported!
export .RE: Regex, matched, @prosite_str

Note that this is really symmetrical to import. So, I think it would be very nice if we had that.

timholy commented 8 years ago

@bicycle1885, if should work if RE exports Regex, matched, and @prosite_str, say using RE inside Seq, and then have Seq re-export all those same things. The Reexport package makes this a little nicer.

This issue is specific to a case where module A has its own save method but wishes to export a save method from B instead.

bicycle1885 commented 8 years ago

@timholy Thank you. I know it is possible, but I'd like to export as I wrote above. Names exported by the RE module is not in its parent module (Seq). It is also hard to keep multiple export statements synchronized. Reexport.jl works file, but I think this should be supported in the Julia compiler.

It seems to me that your issue and my issue are very similar and can be solved in one fix. Should I open a new issue about my stuff?

timholy commented 8 years ago

Simpler example:

julia> module A

       export fancy
       fancy(x) = 2x

       end
A

julia> module B

       import A
       fancy(x) = 3x
       export A.fancy

       end

ERROR: syntax: extra token "." after end of expression

Here I'd like to have using B make available A's version of fancy.

Ref https://github.com/JuliaLang/METADATA.jl/pull/6619#issuecomment-251479159

vtjnash commented 7 years ago

dup #1986

timholy commented 7 years ago

While I see how my description seems similar to #1986, it's not really the same thing; I've clarified an important distinction in the title. I have no problems with listing the specific methods I want to export, I just want to control the module scope of the exports explicitly. But it's a parser error:

julia> module A
       export foo
       foo() = 1
       end
A

julia> module B
       using A
       foo() = 2
       export A.foo
ERROR: syntax: extra token "." after end of expression

Didn't even let me finish the module definition. We're allowed to say import A.foo, why not export A.foo?

timholy commented 7 years ago

I'm not a schemer (and would have to re-teach myself the language to tackle this myself), but I suspect the fix is here.

JeffBezanson commented 7 years ago

To clarify, I assume you want B.foo() to return 2, but the foo you get from using B to return 1? If so, we can't support that without some redesign. A module can have only one binding for a name, and the same one is used for using and dot access.

timholy commented 7 years ago

Sorry I missed your reply. Yes, you understood the request precisely. I hear you on the challenges. Presumably not 1.0 material? Or even 1.x? I recognize the potential for confusion here.