Determining atom or module at runtime?

598 views
Skip to first unread message

...Paul

unread,
Jul 21, 2016, 1:20:46 PM7/21/16
to elixir-lang-talk
Not saying this is a great idea, but I'm working with a pattern where a parameter can be a module or a string.  The gist is:

def do_it(data, foo) when is_bitstring(foo), do: data
def do_it(data, foo) do
  foo.do_other_thing(data)
end

Works great, but what if I want to allow actual atoms to be used, similar to strings?  How can I identify the difference between an actual atom, like :bar, and a module, like Bar?

is_atom() returns true for both cases (makes sense because module names are basically atoms, :"Elixir.Bar")  Is there a way to tell the difference with a guard?

If not a guard, some Kernel or Module function?

...Paul


OvermindDL1

unread,
Jul 21, 2016, 1:26:53 PM7/21/16
to elixir-lang-talk
Atoms and ModuleNames are one and the same, no difference, thus:

There is no way to tell the difference between them without calling a function on them (`module_info\1` is a good one since all modules have one, could just test if the atom/module has that function), however this cannot be done in a guard, so you'd have to test it in the body, then perhaps delegate out to another function set.

OvermindDL1

unread,
Jul 21, 2016, 1:27:51 PM7/21/16
to elixir-lang-talk
Also, unless you specifically want to support bitstrings (non-aligned binaries), then you probably want your guard to be `is_binary/1` instead of `is_bitstring/1`.  :-)

Paul Clegg

unread,
Jul 21, 2016, 1:51:43 PM7/21/16
to elixir-l...@googlegroups.com
On Thu, Jul 21, 2016 at 10:27 AM, OvermindDL1 <overm...@gmail.com> wrote:
Also, unless you specifically want to support bitstrings (non-aligned binaries), then you probably want your guard to be `is_binary/1` instead of `is_bitstring/1`.  :-)

Dagnabbit.  I thought bitstring was what appropriately handled wide characters, and binary could be anything.  That's a wee bit confusing, that bitstrings are actually "random binaries" and binaries are "strings"?  :P

BTW, thanks for the tip on using module_info!

...Paul


Theron Boerner

unread,
Jul 21, 2016, 2:14:30 PM7/21/16
to elixir-lang-talk
Use this:

iex(7)> :erlang.get_module_info(String, :module)
String
iex(8)> :erlang.get_module_info(:lists, :module)
:lists
iex(9)> :erlang.get_module_info(:walrus, :module)
** (ArgumentError) argument error
    :erlang.get_module_info(:walrus, :module)

OvermindDL1

unread,
Jul 21, 2016, 2:15:39 PM7/21/16
to elixir-lang-talk
Technically:
bitstring -> A binary that is 1-bit aligned, so it could be, say, 6 bits long, or 35 bits, or whatever.
binary -> A binary that is 8-bit aligned, this is the one that should almost always be used unless you have some specialized network packet parsing or so, this is also what a String is in Elixir.
A bitstring is a binary (so strings would go there), however if you gave it a binary that was not 8-bit aligned then most things trying to use it would loudly break.  A binary is not a bitstring.  Thus if you only want Strings then use binary.  :-)

OvermindDL1

unread,
Jul 21, 2016, 2:20:02 PM7/21/16
to elixir-lang-talk
That requires dealing with an error and having a try/catch and it is just a pain.  As I mentioned before, testing if the function `module_info/0` exists I think is a great way:
```elixir
iex(9)> :erlang.function_exported(:blah, :module_info, 0)
false
iex(10)> :erlang.function_exported(String, :module_info, 0)
true
```

Theron Boerner

unread,
Jul 21, 2016, 2:28:28 PM7/21/16
to elixir-lang-talk
True, OvermindDL1's is the way to go unless you need info about the module. Anyone know why this doesn't work?:

iex(11)> :erlang.function_exported(GenEvent.Stream, :module_info, 0)
false

or

iex(1)> :erlang.get_module_info(GenEvent.Stream, :module)
** (ArgumentError) argument error
    :erlang.get_module_info(GenEvent.Stream, :module)

Could it be because it defined a struct?

--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/9fed8269-f58f-4d65-b369-ea6477b2126a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

OvermindDL1

unread,
Jul 21, 2016, 2:32:43 PM7/21/16
to elixir-lang-talk
Nope, something is borked for you:
```elixir
iex(12)> :erlang.function_exported(GenEvent.Stream, :module_info, 0)
true
```

Theron Boerner

unread,
Jul 21, 2016, 2:36:16 PM7/21/16
to elixir-lang-talk
What Elixir/OTP version are you running? I'm on OTP 19, Elixir 1.3.2

OvermindDL1

unread,
Jul 21, 2016, 2:41:18 PM7/21/16
to elixir-lang-talk
Oh, and I might know why, the module might not be loaded yet.  Until code is actually called in the module then it is not loaded into the VM, but once loaded it stays loaded, I actually have something that manages that in my Plugin system that handles dynamic Module detection and loading:
```elixir
iex(14)> ExPlugins.get_plugins(MyServer.PermsBehaviour)
[MyServer.Perms.Auth.Permission, MyServer.Perms.Auth.Profile,
 MyServer.Perms.Auth.Users, MyServer.Perms.Auth, MyServer.Perms.Help.Issue,
 MyServer.Perms.Help, MyServer.Perms.Messenger,
 MyServer.Perms.SomeOtherSection.Report, MyServer.Perms.SomeOtherSection.Requirement,
 MyServer.Perms.SomeOtherSection.Semester, MyServer.Perms.SomeOtherSection.Student.Requirement,
 MyServer.Perms.SomeOtherSection.Student.Semester, MyServer.Perms.SomeOtherSection.Student,
 MyServer.Perms.SomeOtherSection, MyServer.Perms.Tag]
```
It is my Elixir port of my old Erlang plugin system (so much easier to write here).  It detects what module implement a behaviour, verify they are loaded into the system, if not it loads them, then returns a list of them (while caching the results for faster response later along with being able to purge the cache via `ExPlugin.clear_cache(MyServer.PermsBehaviour)` or just `ExPlugin.clear_cache()` to clear everything.  But yes, I had to load the modules properly to detect their information.

OvermindDL1

unread,
Jul 21, 2016, 2:43:18 PM7/21/16
to elixir-lang-talk
Confirmed it is what I thought it was:
```elixir
iex(16)> :erlang.function_exported(:observer, :module_info, 0)
false
iex(17)> :observer.start
:ok
iex(18)> :erlang.function_exported(:observer, :module_info, 0)
true
```

José Valim

unread,
Jul 21, 2016, 2:52:19 PM7/21/16
to elixir-l...@googlegroups.com
Code.ensure_loaded? will load the module if one os available. However I would strongly suggest to stop guessing and keep atoms and modules apart, maybe by passing a tuple. Otherwise, what if you want to pass an atom and it happens to have the same name as an Erlang module?
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/ea35fb1e-a2b6-4029-9425-ea71d265ee24%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.


--


José Valim
Skype: jv.ptec
Founder and Director of R&D

OvermindDL1

unread,
Jul 21, 2016, 3:17:20 PM7/21/16
to elixir-lang-talk, jose....@plataformatec.com.br
Exceedingly true yeah, tagging data is always best.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-talk+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages