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

Module at top level scope #21009

Closed
bramtayl opened this issue Mar 13, 2017 · 13 comments
Closed

Module at top level scope #21009

bramtayl opened this issue Mar 13, 2017 · 13 comments

Comments

@bramtayl
Copy link
Contributor

julia> begin
           module A
           1
           end
       end
ERROR: syntax: module expression not at top level

Theoretically this should work, because module A is indeed in global scope? Would be helpful to get working for macros which operate on large blocks of global code

@JeffBezanson
Copy link
Member

You can do this by returning an expression with head :toplevel.

@bramtayl
Copy link
Contributor Author

bramtayl commented Mar 13, 2017

I managed to get this working like this:

is_module(a) = false
is_module(e::Expr) = e.head == :module

move_module_to_global(e::Expr) = 
    if e.head == :block && any( is_module.(e.args) )
        Expr(:toplevel, e.args...)
    elseif e.head == :block
        Expr(:block, move_module_to_global.(e.args)...)
    else 
        e 
    end

macro identity(e)
    e |> move_module_to_global |> esc
end

@identity begin
    module A 
    b = 1
    end 
    A.b
end

Which works for my purposes, but I think its worth including something like this in base

@JeffBezanson
Copy link
Member

I think this is mostly a duplicate of #2586. It's tricky, since it's not generally correct to just replace block with toplevel, or to move code around.

@bramtayl
Copy link
Contributor Author

Actually, the solution above has a slight issue. Even though it correctly defines the module, it returns nothing

@mauro3
Copy link
Contributor

mauro3 commented Mar 15, 2017

I thought that begin...end blocks are supposed to have no impact on scope. That is at least what the docs say. Or does "top level" refer to some other thing? (Note that if suffers the same issue)

@yuyichao
Copy link
Contributor

This is not scope. And Expr(:toplevel) is not begin end.

@mauro3
Copy link
Contributor

mauro3 commented Mar 15, 2017

Maybe the error could be more specific on what "top level" it is?

@JeffBezanson
Copy link
Member

Also related to #10472. If local were not allowed in global scope, then begin end could indeed be equivalent to a :toplevel expr.

@bramtayl
Copy link
Contributor Author

A seemingly easy fix would just be to pass through code post macro-parsing pre-lowering and (repeatedly) remove any begin end bookends at top-level.

@jamesjscully
Copy link

Any updates? I'm using 7.0 alpha and am having similar problems.
I'm trying to implement a macro that creates types of integers modulo n and extend base.+ and base.*.
I'm have:

abstract type Zmod <: Integer end
macro constructZmodn(n)
    name = parse("Z$n", raise = true)
    primitive type name <: Zmod 8 end
    return quote
        #define reinterpret behavior
        #define ops
    end
end
@constructZmodn(3)

But I'm getting the error

type definition not allowed inside a local scope

I've read through the linked issues here but cant find a good solution. I don't need the types to exist in a local scope but I want to define them within one.

This seems like it would be a pretty common pattern in a mathematically oriented language. I know it will be key if I want to write constructors for derivative algebraic structures that use the dispatch system.

@vtjnash
Copy link
Member

vtjnash commented Jun 8, 2018

You could just use eval directly and do:

abstract type Zmod <: Integer end
function constructZmodn(n)
    name = parse("Z$n", raise = true)
    eval(:(primitive type $name <: Zmod 8 end))
    #eval reinterpret behavior
    #eval ops
end
constructZmodn(3)

@jamesjscully
Copy link

I figured that I wouldn't be able to define methods on types that didn't exist at parse time. Is this false?
I got it to work with:

macro constructZmodn(n)
    name = parse("Z$n")
    esc(quote
        primitive type $(name) <: Zmod 8 end
        Base.convert(::Type{$(name)}, x::T) where {T<:Integer} = reinterpret($name, x % $n)
        Base.convert(::Type{UInt8}, x::$(name)) = reinterpret(UInt8, x)        
        $(name)(x::T) where {T <: Integer} = reinterpret($(name),x)
    end)
end

But the weird thing is if I define more methods in a for loop within the same esc(quote ... end) block, I get an error saying that type definitions aren't allowed in local scope. Needless to say this is very counter-intuitive behavior. Here's the code that doesn't work. (on 6.3 now because my 7.0 alpha install broke inadvertently)

macro constructZmodn(n)
    name = parse("Z$n")
    esc(quote
        primitive type $(name) <: Zmod 8 end
        Base.convert(::Type{$(name)}, x::T) where {T<:Integer} = reinterpret($name, x % $n)
        Base.convert(::Type{UInt8}, x::$(name)) = reinterpret(UInt8, x)        
        $(name)(x::T) where {T <: Integer} = reinterpret($(name),x)
        for op in [:+,:-,:*,:/]
            (op)(x::$(name), y::$(name)) = op(x,y) % $n
        end
    end)
end

However, If I take for loop out of the escape block and escape inside the loop everything works as expected:

macro constructZmodn(n)
    name = parse("Z$n")
    esc(quote
        primitive type $(name) <: Zmod 8 end
        Base.convert(::Type{$(name)}, x::T) where {T<:Integer} = reinterpret($name, x % $n)
        Base.convert(::Type{UInt8}, x::$(name)) = reinterpret(UInt8, x)        
        $(name)(x::T) where {T <: Integer} = reinterpret($(name),x)
    end)
    for op in [:+,:-,:*,:/]
        esc(:((op)(x::$(name), y::$(name)) = op(x,y) % $n))
    end
end

@jamesjscully
Copy link

Actually it doesn't work in the last example. The constructor Z6 is not in the global namespace. It only appeared to be because I didn't restart the REPL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants