Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Map device name in runtime to save some typing #366

Merged
merged 1 commit into from

3 participants

@alco
Owner

Here's my take on the issue #318. I ran into a compiler crash when placing the defmacrop part at the bottom of the module.

elixir/lib/io.ex:114: macro map_dev/1 is unused
error: {'__MAIN__.CompileError','__exception__',
                                <<"function map_dev/1 undefined">>,
                                <<"/Users/alco/Documents/git/elixir/lib/io.ex">>,
                                19}
stacktrace: [{io,'__info__',[macros],[]},
             {elixir_dispatch,get_optional_macros,1,
                              [{file,"src/elixir_dispatch.erl"},{line,225}]},
             {elixir_dispatch,expand_require,8,
                              [{file,"src/elixir_dispatch.erl"},{line,135}]},
             {elixir_dispatch,dispatch_require,6,
                              [{file,"src/elixir_dispatch.erl"},{line,83}]},
             {lists,mapfoldl,3,[{file,"lists.erl"},{line,1278}]},
             {elixir_clauses,assigns_block,6,
                             [{file,"src/elixir_clauses.erl"},{line,44}]},
             {elixir_def,translate_definition,7,
                         [{file,"src/elixir_def.erl"},{line,165}]},
             {elixir_def,store_definition,8{"init terminating in do_boot",1}
,
                         [{file,"src/elixir_def.erl"},{line,81}]}]

Also, I tried to make a wrapper for def to streamline the mapping experience, but got stuck with unquoting and concatenating module refs. Not sure if it's of any use:

  defmacrop ErlIO(fun, dev, args) do
    quote do
      Erlang.io.unquote(fun) unquote(map_dev(dev)), unquote_splicing(args)
    end
  end
@travisbot

This pull request passes (merged 0872515 into c79caec).

@josevalim
Owner

This looks great, thanks. Is there really a need to be a macro? Just wondering.

And yes, a macro must be defined before it is used. :) The error message was ugly just because you were compiling elixir itself, you would get a nice pretty-printed message otherwise.

@josevalim
Owner

Regarding the def wrapper you had in mind, you would have to use apply:

defmacrop ErlIO(fun, dev, args) do
  quote do
    apply Erlang.io, unquote(fun), [unquote(map_dev(dev)), unquote_splicing(args)]
  end
end
@josevalim josevalim merged commit fb07d5e into elixir-lang:master
@alco
Owner

This looks great, thanks. Is there really a need to be a macro? Just wondering.

The macro is saving us one function call, although it's more a matter of preference.

Regarding the def wrapper you had in mind, you would have to use apply:

That's sad, I thought I would be able to do alias concatenation at compile time.

@josevalim
Owner

@alco apply is a smart macro and actually does it at compile time, so no worries. :)

@alco
Owner

@josevalim Oh, sorry. I thought you were talking about Erlang's apply. Thanks for the info :)

@josevalim
Owner

Yeah, we simply wrap erlang's apply. They may also optimize it too, for example, apply(lists, flatten, [[1,2,3]]) may be the same as lists:flatten([1,2,3]), never benchmarked to be sure though.

@alco
Owner

@josevalim They have a note here:

Note: If the number of arguments are known at compile-time, the call is better written as Module:Function(Arg1, Arg2, ..., ArgN).

I understand it as a statement that apply does some work at runtime to support variable-length lists of arguments.

@josevalim
Owner

Interesting. They could optimize it, it is funny they don't. Elixir optimizes it exactly if the list of arguments is known at compile-time (which is the case above).

@alco alco deleted the alco:map_dev branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 13, 2012
  1. @alco
This page is out of date. Refresh to see the latest.
Showing with 28 additions and 16 deletions.
  1. +28 −16 lib/io.ex
View
44 lib/io.ex
@@ -4,6 +4,17 @@ defmodule IO do
are expected to be in unicode.
"""
+ # Map the Elixir names for standard io and error to Erlang names
+ defmacrop map_dev(dev) do
+ quote do
+ case unquote(dev) do
+ :stdio -> :standard_io;
+ :stderr -> :standard_error;
+ other -> other;
+ end
+ end
+ end
+
@doc """
Reads `count` bytes from the IO device. It returns:
@@ -15,8 +26,8 @@ defmodule IO do
for instance {:error, :estale} if reading from an
NFS file system.
"""
- def read(device // :standard_io, count) do
- Erlang.io.get_chars(device, "", count)
+ def read(device // :stdio, count) do
+ Erlang.io.get_chars(map_dev(device), "", count)
end
@doc """
@@ -30,8 +41,8 @@ defmodule IO do
for instance {:error, :estale} if reading from an
NFS file system.
"""
- def readline(device // :standard_io) do
- Erlang.io.get_line(device, "")
+ def readline(device // :stdio) do
+ Erlang.io.get_line(map_dev(device), "")
end
@doc """
@@ -51,13 +62,13 @@ defmodule IO do
#=> "error"
"""
- def write(device // :standard_io, item) do
- Erlang.io.put_chars device, item
+ def write(device // :stdio, item) do
+ Erlang.io.put_chars map_dev(device), item
end
- def print(device // :standard_io, item) do
+ def print(device // :stdio, item) do
IO.puts "IO.print is deprecated in favor of IO.write"
- Erlang.io.put_chars device, item
+ Erlang.io.put_chars map_dev(device), item
end
@doc """
@@ -65,16 +76,17 @@ defmodule IO do
but adds a new line at the end. The argument is expected
to be a chardata.
"""
- def puts(device // :standard_io, item) do
- Erlang.io.put_chars device, item
- Erlang.io.nl(device)
+ def puts(device // :stdio, item) do
+ erl_dev = map_dev(device)
+ Erlang.io.put_chars erl_dev, item
+ Erlang.io.nl(erl_dev)
end
@doc """
Inspects and writes the given argument to the device
followed by a new line.
"""
- def inspect(device // :standard_io, item) do
+ def inspect(device // :stdio, item) do
puts device, Elixir.Builtin.inspect(item)
end
@@ -89,8 +101,8 @@ defmodule IO do
for instance {:error, :estale} if reading from an
NFS file system.
"""
- def getb(device // :standard_io, prompt, count // 1) do
- Erlang.io.get_chars(device, prompt, count)
+ def getb(device // :stdio, prompt, count // 1) do
+ Erlang.io.get_chars(map_dev(device), prompt, count)
end
@doc """
@@ -105,7 +117,7 @@ defmodule IO do
for instance {:error, :estale} if reading from an
NFS file system.
"""
- def gets(device // :standard_io, prompt) do
- Erlang.io.get_line(device, prompt)
+ def gets(device // :stdio, prompt) do
+ Erlang.io.get_line(map_dev(device), prompt)
end
end
Something went wrong with that request. Please try again.