Function overlapping specs - erlang

I have a simple function like this:
def extract_text({_, _, [text]}) when is_binary(text), do: text
def extract_text(_), do: nil
and the spec I added for it was:
#spec extract_text(any) :: nil
#spec extract_text({any, any, [text]}) :: text when text: String.t
but when I run dializer, I get the following error:
lib/foo/bar.ex:1: Overloaded contract for
'Elixir.Foo.Bar':extract_text/1 has overlapping domains;
such contracts are currently unsupported and are simply ignored
I think I understand the reason for it but I can't really come up with a solution. What would be the right spec for this function?

You should be aware of that, even if you define multiple functions of the same arity (accept the same number of arguments), from outside world this is considered only one function. That means, you need to define function signature, and only this one should have type spec defined.
Try the following:
#spec extract_text(any) :: String.t | nil
def extract_text(arg)
def extract_text({_, _, [text]}) when is_binary(text), do: text
def extract_text(_), do: nil

Related

Rspec: Checking the content of a system call

I have a Rake task in my Rails project which executes openssl through a system call.
The code looks like this:
system('bash', '-c', 'openssl cms -verify...')
I need to run the command in bash rather than dash (which is default on Ubuntu) to use process substitution in the command.
I need to create a test with rspec which checks that, in this case, the argument verify was passed as expected.
I have tried the following:
expect(Kernel).to receive(:system) do |args|
expect(args[2]).to match(/verify/)
end
However, this only gives me the third letter in the first string sent to system - i.e. the letter s from bash - rather than the third argument sent in the system call.
What am I doing wrong here? Any suggestions would be much appreciated.
Args are being passed to the block as sequential arguments, so if you want to treat them as an array, you need a splat operator in do |*args|:
expect(Kernel).to receive(:system) do |*args|
expect(args[2]).to match(/verify/)
end
Just to take a step back, it's important to understand how block arguments work, since they are different from methods. For example:
def my_fn(*args)
yield(*args)
end
my_fn(1,2,3) { |args| print args }
# => 1
my_fn(1,2,3) { |a, b, c| print [a,b,c] }
# => [1,2,3]
my_fn(1,2,3) { |*args| print args }
# => [1,2,3]
So if you did do |args| (without the splat), you are assigning the args variable to the first argument passed to the block ("bash") and ignoring the other arguments.

Is there an equivalent to __MODULE__ for named functions in Elixir/ Erlang?

Is there an equivalent for retrieving the name of a function just like like __MODULE__ retrieves the name of a Module in Elixir/Erlang?
Example:
defmodule Demo do
def home_menu do
module_name = __MODULE__
func_name = :home_menu
# is there a __FUNCTION__
end
End
EDITED
The selected answer works,
but calling the returned function name with apply/3 yields this error:
[error] %UndefinedFunctionError{arity: 4, exports: nil, function: :public_home, module: Demo, reason: nil}
I have a function :
defp public_home(u, m, msg, reset) do
end
The function in question will strictly be called within its module.
Is there a way to dynamically call a private function by name within its own module?
▶ defmodule M, do: def m, do: __ENV__.function
▶ M.m
#⇒ {:m, 0}
Essentially, __ENV__ structure contains everything you might need.
Yes, there is. In Erlang there are several predefined macros that should be able to provide the information you need:
% The name of the current function
?FUNCTION_NAME
% The arity of the current function (remember name alone isn't enough to identify a function in Erlang/Elixir)
?FUNCTION_ARITY
% The file name of the current module
?FILE
% The line number of the current line
?LINE
Source: http://erlang.org/doc/reference_manual/macros.html#id85926
To add to Aleksei's answer, here is an example of a macro, f_name(), that returns just the name of the function.
So if you use it inside a function, like this:
def my_very_important_function() do
Logger.info("#{f_name()}: about to do important things")
Logger.info("#{f_name()}: important things, all done")
end
you will get a log statement similar to this:
my_very_important_function: about to do important things
my_very_important_function: important things, all done
Details:
Here is the definition of the macro:
defmodule Helper do
defmacro f_name() do
elem(__CALLER__.function, 0)
end
end
(__CALLER__ is just like __ENV__, but it's the environment of the caller.)
And here is how the macro can be used in a module:
defmodule ImportantCodes do
require Logger
import Helper, only: [f_name: 0]
def my_very_important_function() do
Logger.info("#{f_name()}: doing very important things here")
end
end

What is this Elixir message means

I am writing Elixir to get record from remote nodes, I have write a module,
defmodule Connect do
def connect do
node_ap_dev_ejd = :'abc#abc.com'
:net_adm.ping(node_ap)
fbUsersFun = fn(x) -> :binary.part(x,{0,3}) == <<"*ab">> end
f = fn()-> :mnesia.dirty_select(:'cz_map',[{{:cz_map, :'$1', :'_',:'_',:'_'},[],[:'$1']}]) end
fbUserList = :rpc.call(node_ap_dev_ejd,:mnesia,:activity,[:async_dirty,f])
list = Enum.filter(fbUserList ,fbUsersFun)
length(list)
end
end
I can run the code if I put it in iex shell line by line, however if I compile the code and run Connect.connect , this error appear, I have no idea of it, please suggest
** (Protocol.UndefinedError) protocol Enumerable not implemented for
{:badrpc, {:EXIT, {:undef, [{#Function<1.96315226/0 in Connect.connect/0>, [], []}, {:mnesia_tm, :non_transaction, 5, [file: 'mnesia_tm.erl', line: 738]}]}}}
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:112: Enumerable.reduce/3
(elixir) lib/enum.ex:666: Enum.filter/2
second_function.ex:10: Connect.connect/0
It means that the Enumerable protocol is not implemented for the data {:badrpc, ...}.
Most likely, that error comes from this line:
list = Enum.filter(fbUserList ,fbUsersFun)
In that line, you're trying to filter fbUserList which I guess is {:badrpc, ...} instead of an enumerable. Tuples are not enumerables; lists and maps (and other things) are.
The solution probably lies in a case expression which checks the result returned by :rpc.call/4 in order to defend from errors:
case :rpc.call(node_ap_dev_ejd, :mnesia, :activity, [:async_dirty, f]) do
{:badrpc, _} -> raise "bad rpc error"
fbUserList -> Enum.filter(fbUserList, ...) # and so on
end
I'm having the same issue working mnesia with erlang, the error is because of the anonymous function "f", the thing is that the remote node does not recognize that function because it was created in another node.
EDIT:
I managed to solve the problem in erlang, I will show you how I did it in erlang, I don't know much about elixir but I´m sure if it can be done in erlang it will in elixir.
So this segment
f = fn()-> :mnesia.dirty_select(:'cz_map',[{{:cz_map, :'$1', :'_',:'_',:'_'},[],[:'$1']}]) end
fbUserList = :rpc.call(node_ap_dev_ejd,:mnesia,:activity,[:async_dirty,f])
In erlang is like this
f = fun()-> mnesia:dirty_select(cz_map,[{{cz_map, '$1', '_', '_', '_'},[],['$1']}]) end,
fbUserList = rpc:call(node_ap_dev_ejd, mnesia, activity, [async_dirty, f])
Instead declaring an anonymous fun you have to do something like this
fbUserList = rpc:call(node_ap_dev_ejd, mnesia, activity, [async_dirty, mnesia:dirty_select/2, [cz_map, [{{cz_map, '$1', '_', '_', '_'},[],['$1']}]]])
You can find a clear explanation here what kind of types can be sent on an erlang message?
I hope this information helps you.

A Lua iterator that fails silently?

I have a very simple problem with a simple iterator.
Let's say that I design a function, files(), that iterates over all the files in a folder:
for file in files("/path/to/folder") do
print(file)
end
Now, this seems perfect, but there's a problem here: What if the the folder doesn't exist, or we don't have read permission to it?
How would we indicate such error?
One solution would be to have files() return nil, "no read permission" in this case. We'd then be able to wrap the call to files() inside assert():
for file in assert(files("/path/to/folder")) do
print(file)
end
This seemingly solves the problem. But this forces our users to always use assert(). What if the user doesn't care about errors? For this kind of users we'd want our files() to behave as if the folder is empty. But Lua --in case files() indicates error-- would try to call the returned nil and this will result in an error ("attempt to call a nil value").
So,
How can we design an iterator, files(), that would cater to both users that care about errors and users that don't?
If it's not possible, what alternative would you suggest?
First: Instead of returning nil + error message consider raising an error in the files function (using error). This way you can't forget the assert call, and you won't get the confusing "attempt to call a nil value" error.
You could pass an extra boolean parameter to files when you don't want to raise errors -- you should return an empty function (function() end) instead of calling error in this case.
A more general approach is the following:
-- an iterator that immediately stops a for loop
local function dummy_iter() end
-- catch errors and skip for loop in that case
function iterpcall( g, ... )
local ok, f, st, var = pcall( g, ... )
if ok then
return f, st, var
else
return dummy_iter
end
end
for file in iterpcall( files, "/path/to/folder" ) do
print( file )
for line in iterpcall( io.lines, file ) do -- works for other iterators as well
print( line )
end
end
The implementation of iterpcall above only handles errors raised in the iterator generator (files or io.lines), not in the iterator function (f) itself. You would have to wrap f in a closure with a pcall to do that.
There also question what you whant to do if you get error in the middle of iteration (e.g. access deny for subfolder with recurcive iteration). In this case assert does not help.
In this case I create 2 variant of iterators (inner and outer).
-- raise error
for file in files(...) do ... end
-- return error
files(...,function(file) ... end)
Or just create 2 different iterators.

In Erlang, how do you invoke a function dynamically?

I want to call xyz with the name of a function to be invoked.
-module(sample).
-export([xyz/1]).
xyz(Name) -> Name().
p() -> "you called p".
g() -> "you called g".
But I get the following error:
1> c(sample.erl).
./sample.erl:6: Warning: function p/0 is unused
./sample.erl:7: Warning: function g/0 is unused
{ok,sample}
2> sample:xyz('p').
** exception error: bad function p
in function sample:xyz/1
3>
It is correct that you have to export p and g. You can then use apply/3 to call it.
erlang:apply(sample, p, []).
Only fun-values are usable with the Fun(...) syntax. You are passing in an atom-value. An atom is a 'bad function' as the error message go. You could do something similar to
xyz(p) -> fun p/0;
xyz(g) -> fun g/0.
Then go ahead and call
Fun = xyz(p),
Fun()
-module(sample).
-export([xyz/1, p/0, g/0]).
xyz(Name) -> ?MODULE:Name().
p() -> "you called p".
g() -> "you called g".
1> sample:xyz(p).
"you called p"
Pattern match is the idiom to use:
-module(sample).
-export([xyz/1]).
xyz(p) -> p();
xyz(q) -> g().
p() -> "you called p".
g() -> "you called g".
If you want to be dynamic you can use a gen_event server.
Essentially what this is is a server that holds a state which consists of key/function pair like so:
[{p, #func1},
{g, #func2},
{..., ...},
...]
You can then essentially bind events to functions. (there is, needless to say, a bit more to it than that.
The easiest way to do is to try exporting p and g along with xyz.
-export([xyz/1, p/0,g/0]).
After exporting the function p and g can be called as follows :
1> sample:xyz(fun sample:p/0).
"you called p"
2> sample:xyz(fun sample:g/0).
"you called g"
Another way to look at it is that (depending on the problem you're solving) dynamic calls to functions isn't necessarily the right approach. Given that processes and message passing are the way you organize your code in Erlang since it's a "concurrency oriented language", maybe you could just use message passing with a selective receive rather than mimicking idioms of a sequential language? Send a message for what you want and get the custom reply based on that. It's about the result of each function, not the function itself, after all. (Plus there's the flexibility and scalability of message passing, etc.)
Although processes aren't totally free compared to calling from a library module, Erlang-level processes are dirt cheap (especially if the message communication is within the same node). They're not OS-level processes. The overhead would be comparable (or better) to dynamic function calls and object instantiation in heavier scripting languages.

Resources