Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Implement a simple version of the t helper for the interactive shell #609

Merged
merged 1 commit into from

3 participants

@yrashk

Prints out type/spec information

(If Haskell has :t, why can't we have it, too?)

@yrashk
iex(15)> t Enum.t/0 
@type(t() :: Enum.Iterator.t())
:ok
iex(16)> t Enum.all?/2
@spec(all?(t(), fun(element()) do
  boolean()
end)) do
  boolean()
end
:ok
@devinus

This is really nice.

@yrashk yrashk Implement a simple version of the t helper for the interactive shell …
…(prints out type/spec information)

Also implements corresponding helper functions in Kernel.Typespec
c003817
@josevalim
Owner

I love this! Who is working on Googlex? ;) http://www.haskell.org/hoogle/

I believe the code is good, there is just one concern. Today, all in Kernel.Typespec is related to typespecs when a module is being defined. Those new function play directly with the abstract beam code and I wouldn't put them together. Some options are:

1) Provide __info__(:spec), __info__(:type) and others that would retrieve those from the beam transparently

2) Create a module specifically for working with beam files and retrieving information from it

PS: @yrashk you don't need to do these changes yourselves, I would just like to figure it out so I can merge and move it to the proper place.

@yrashk

I tend to favour 2) but this is just a small set of helpers over beam_lib, highly related to typespecs only. I believe it might very well belong to Kernel.Typespec as this is related to Kernel.Typespec.to_binary/1

Can we merge this ASAP? Ever since I wrote it I keep missing while debugging typespec related issues in other branches

@yrashk

(and... may be we can merge first, relocate functions [if necessary at all] later?)

@yrashk

We can re-introduce Typespec module if you want?

@josevalim josevalim merged commit 3fac73e into elixir-lang:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 1, 2012
  1. @yrashk

    Implement a simple version of the t helper for the interactive shell …

    yrashk authored
    …(prints out type/spec information)
    
    Also implements corresponding helper functions in Kernel.Typespec
This page is out of date. Refresh to see the latest.
View
77 lib/elixir/lib/kernel/typespec.ex
@@ -237,6 +237,83 @@ defmodule Kernel.Typespec do
quote do: []
end
+ @doc """
+ Retrieves all type specifications from a module.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def types(module) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :type, ti} inlist abstract_code, do: {:type, ti}
+ [] ->
+ []
+ end
+ end
+
+ @doc """
+ Retrieves all type specifications from a module with a given name and arity.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def types(module, type, arity) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :type, {t, _, a} = ti} inlist abstract_code, t == type, length(a) == arity, do: {:type, ti}
+ _ ->
+ []
+ end
+ end
+
+
+ @doc """
+ Retrieves all functions specifications from a module.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def specs(module) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :spec, {fa, s}} inlist abstract_code, do: {{:spec, fa}, s}
+ _ ->
+ []
+ end
+ end
+
+ @doc """
+ Retrieves all functions specifications from a module for a given name and arity.
+
+ The module has to have a corresponding beam file on the file system.
+ """
+ def specs(module, function, arity) do
+ case abstract_code(module) do
+ {:ok, abstract_code} ->
+ lc {:attribute, _, :spec, {{f,a}=fa, s}} inlist abstract_code, f == function, a == arity, do: {{:spec, fa}, s}
+ _ ->
+ []
+ end
+ end
+
+
+ defp abstract_code(module) do
+ case :beam_lib.chunks(abstract_code_beam(module), [:abstract_code]) do
+ {:ok, {_, [{:abstract_code, {raw_abstract_v1, abstract_code}}]}} ->
+ {:ok, abstract_code}
+ _ ->
+ :error
+ end
+ end
+
+ defp abstract_code_beam(module) when is_atom(module) do
+ case :code.which(module) do
+ :non_existing -> module
+ file -> file
+ end
+ end
+ defp abstract_code_beam(binary) when is_binary(binary) do
+ binary
+ end
+
## Typespec conversion
# Handle unions
View
28 lib/elixir/test/elixir/typespec_test.exs
@@ -328,4 +328,32 @@ defmodule Typespec.Test.Type do
assert Kernel.Typespec.to_binary(spec) == Macro.to_binary(definition)
end
end
+
+ # test/spec retrieval
+
+ test "specs retrieval" do
+ Code.compiler_options debug_info: true
+ { :module, T, binary, _ } = defmodule T do
+ @spec a, do: any
+ def a, do: nil
+ end
+ Code.compiler_options debug_info: false
+ :code.delete(T)
+ :code.purge(T)
+ assert [{{:spec,{:a,_}},[{:type,_,:fun,[{:type,_,:product,[]},{:type,_,:any,[]}]}]}] = Kernel.Typespec.specs(binary)
+ assert [{{:spec,{:a,_}},[{:type,_,:fun,[{:type,_,:product,[]},{:type,_,:any,[]}]}]}] = Kernel.Typespec.specs(binary, :a, 0)
+ end
+
+ test "types retrieval" do
+ Code.compiler_options debug_info: true
+ { :module, T, binary, _ } = defmodule T do
+ @type a :: any
+ end
+ Code.compiler_options debug_info: false
+ :code.delete(T)
+ :code.purge(T)
+ assert [{:type, {:a,{:type,_,:any,[]},[]}}] = Kernel.Typespec.types(binary)
+ assert [{:type, {:a,{:type,_,:any,[]},[]}}] = Kernel.Typespec.types(binary, :a, 0)
+ end
+
end
View
65 lib/iex/lib/iex/helpers.ex
@@ -13,6 +13,7 @@ defmodule IEx.Helpers do
* `c/2` - compiles a file in the given path
* `h/0`,`h/1`, `h/2` - prints help/documentation
+ * `t/1`,`t/3` — prints type specification information
* `m/0` - prints loaded modules
* `r/0` - recompiles and reloads the given module's source file
* `v/0` - prints all commands and values
@@ -213,6 +214,70 @@ defmodule IEx.Helpers do
atom_to_binary(var)
end
+ # FIXME: this can't be shown as h(t/1)
+ @doc """
+ Prints all types and function specifications from a given module
+
+ ## Examples
+
+ t(Enum)
+
+ """
+ def __t_module__(module) do
+ types =
+ lc type inlist Kernel.Typespec.types(module) do
+ IO.puts Kernel.Typespec.to_binary(type)
+ type
+ end
+ specs =
+ lc spec inlist Kernel.Typespec.specs(module) do
+ IO.puts Kernel.Typespec.to_binary(spec)
+ spec
+ end
+ if specs == [] and types == [] do
+ IO.puts "No type specifications for #{inspect module} has been found"
+ end
+ :ok
+ end
+
+ @doc """
+ Prints the type specification for a given function or a type
+
+ ## Examples
+
+ t(Enum.all?/2)
+ t(Enum.t/0)
+
+ """
+ def t(module, function, arity) do
+ types =
+ lc type inlist Kernel.Typespec.types(module, function, arity) do
+ IO.puts Kernel.Typespec.to_binary(type)
+ type
+ end
+ specs =
+ lc spec inlist Kernel.Typespec.specs(module, function, arity) do
+ IO.puts Kernel.Typespec.to_binary(spec)
+ spec
+ end
+ if specs == [] and types == [] do
+ IO.puts "No type specification for #{inspect module}.#{function}/#{arity} has been found"
+ end
+ :ok
+ end
+
+ defmacro t({ :/, _, [{ { :., _, [mod, fun] }, _, [] }, arity] }) do
+ quote do
+ t(unquote(mod), unquote(fun), unquote(arity))
+ end
+ end
+ defmacro t(module) do
+ quote do
+ __t_module__(unquote(module))
+ end
+ end
+
+
@doc """
Retrieves nth query's value from the history. Use negative
values to lookup query's value from latest to earliest.
Something went wrong with that request. Please try again.