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

Avoiding Naming Collisions with Namespaces #13203

Closed
noraj opened this issue Mar 20, 2023 · 3 comments
Closed

Avoiding Naming Collisions with Namespaces #13203

noraj opened this issue Mar 20, 2023 · 3 comments

Comments

@noraj
Copy link
Contributor

noraj commented Mar 20, 2023

Bug Report

I'm sorry if it is not a bug, as the variable workaround works in ruby and not in crystal I thought this could be a bug.

Summary

Avoiding Naming Collisions with Namespaces can be solved by setting the built-in class to a variable before defining the custom module class in Ruby, but it doesn't work in Crystal (cf. https://flylib.com/books/en/2.44.1/avoiding_naming_collisions_with_namespaces.html).

Details

Crystal naming collision

test.cr

cat test.cr 
require "socket"

module Test1
  def self.test2(addr : String)
    Socket::IPAddress.new(addr, 8080)
  end

  class Socket
    # something else
  end
end

Test1.test2("127.0.0.1")
$ crystal run test.cr 
Showing last frame. Use --error-trace for full trace.

In test.cr:5:5

 5 | Socket::IPAddress.new(addr, 8080)
     ^----------------
Error: undefined constant Socket::IPAddress

Ruby naming collision

test.rb

require "socket"

module Test1
  def self.test2
    Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
  end

  class Socket
    # something else
  end
end

Test1.test2
$ ruby test.rb
test.rb:5:in `test2': uninitialized constant Test1::Socket::Option (NameError)
        from test.rb:13:in `<main>'

Workaround naming collision in Ruby with variable

test.rb

require "socket"

module Test1
  RubySocket = Socket

  def self.test2
    RubySocket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
  end

  class Socket
    # something else
  end
end

Test1.test2

ruby test.rb (no error)

(Trying to) Workaround naming collision in Crystal with variable

test.cr

require "socket"

module Test1
  CrystalSocket = Socket

  def self.test2(addr : String)
    CrystalSocket::IPAddress.new(addr, 8080)
  end

  class Socket
    # something else
  end
end

Test1.test2("127.0.0.1")
$ crystal run test.cr 
Showing last frame. Use --error-trace for full trace.

In test.cr:7:5

 7 | CrystalSocket::IPAddress.new(addr, 8080)
     ^-----------------------
Error: undefined constant CrystalSocket::IPAddress

Still encounter the same error.

Yes it is possible to rename the class from the custom module

Yes I know it is possible to rename the class in the module (like the code below) to avoid conflicts with the built-in class inside the module scope. But I would rather know how can I explicitly call the built-in Socket in a module scope while there is a class with the same name. Outside of Test1 scope there is no conflict with Socket and Test1::Socket.

require "socket"

module Test1
  def self.test2(addr : String)
    Socket::IPAddress.new(addr, 8080)
  end

  class SocketCustom
    # something else
  end
end

Test1.test2("127.0.0.1")

Env

$ crystal -v
Crystal 1.7.3 (2023-03-07)

LLVM: 14.0.6
Default target: x86_64-pc-linux-gnu
@noraj noraj added the kind:bug A bug in the code. Does not apply to documentation, specs, etc. label Mar 20, 2023
@Blacksmoke16
Copy link
Member

Blacksmoke16 commented Mar 20, 2023

But I would rather know how can I explicitly call the built-in Socket in a module scope while there is a class with the same name. Outside of Test1 scope there is no conflict with Socket and Test1::Socket.

You can just do ::Socket::IPAddress.new(addr, 8080). The :: prefix denotes it should use the top level type and not the local scope one. This feature also works for methods. E.g. ::raise "foo" if you had a locally defined #raise(message) method.

I don't actually think this is really documented anywhere, so if you would open an issue on https://github.com/crystal-lang/crystal-book, that would be 🙏. Going to close this one given it's already a thing.

@Blacksmoke16 Blacksmoke16 closed this as not planned Won't fix, can't repro, duplicate, stale Mar 20, 2023
@Blacksmoke16 Blacksmoke16 added kind:question status:invalid and removed kind:bug A bug in the code. Does not apply to documentation, specs, etc. labels Mar 20, 2023
noraj added a commit to noraj/crystal-book that referenced this issue Mar 30, 2023
@noraj
Copy link
Contributor Author

noraj commented Mar 30, 2023

I PRed the doc: crystal-lang/crystal-book#680

@straight-shoota
Copy link
Member

The top-level scope prefix is actually documented in https://crystal-lang.org/reference/1.7/syntax_and_semantics/the_program.html#top-level-scope

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

No branches or pull requests

3 participants