ashneyderman

ashneyderman

Compiler tells me anonymous functions can not have default parameters

So something like this fails to compile:

def streamfun_from_string(str) do
  fn(skip_headers \\ true) ->
    ..
  end
end

I am wondering why is this a restriction? I do not quite understand the reason for it. Is there a work-around?

Most Liked

OvermindDL1

OvermindDL1

To sum up:

Functions are named by their atom name and their arity. There is no such thing as an arity-less function. There is such a thing as an atom-less function, those are anonymous functions, notice it still has an arity however. Thus every function always absolutely has an arity. Now considering that a default argument actually creates two function of one with the full arity and one of less that just calls the one with more, and considering function pointers only reference a single function and not multiple, that means it can’t encode both, just not possible.

peerreynders

peerreynders

Google Groups elixir-lang-talk (2014): Default arguments on anonymous functions

i.e.

defmodule Demo do
  def identity(x \\ :ok), # ONE function definition
    do: x

  defp info(fun) do
    fun
    |> Function.info()
    |> IO.inspect()
  end
  def run do
    info(&identity/0)  # TWO functions
    info(&identity/1)  #
  end
end

Demo.run()
$ elixir demo.exs
[
  pid: #PID<0.89.0>,
  module: Demo,
  new_index: 0,
  new_uniq: <<135, 214, 25, 143, 240, 101, 78, 237, 14, 92, 136, 181, 46, 33, 8,
    18>>,
  index: 0,
  uniq: 71217356,
  name: :"-run/0-fun-0-",
  arity: 0,
  env: [],
  type: :local
]
[
  pid: #PID<0.89.0>,
  module: Demo,
  new_index: 1,
  new_uniq: <<135, 214, 25, 143, 240, 101, 78, 237, 14, 92, 136, 181, 46, 33, 8,
    18>>,
  index: 1,
  uniq: 71217356,
  name: :"-run/0-fun-1-",
  arity: 1,
  env: [],
  type: :local
]

An anonymous function is a single function value.

Is there a work-around?

Probably not what you had in mind:

defmodule Demo do
  def makeFun() do
    fn options ->
      {skip_headers, _options} = Keyword.pop_first(options, :skip_headers, true)
      skip_headers
    end
  end

  def run do
    f = makeFun()
    IO.inspect(f.([]))
    IO.inspect(f.(skip_headers: false))
  end
end

Demo.run()
$ elixir demo.exs
true
false
OvermindDL1

OvermindDL1

The common erlang idiom is to do {module, function, [postargs]} where postargs are useful for carrying additional state (I.E. the closure part). So you can do something like {Blah, :blorp, [1, 2, 42]} then call it like {mod, fun, args} = {Blah, :blorp, [1, 2, 42]}; apply(mod, fun, [:prefix, :args | args]).

This is also why you tend to see so many erlang functions designed to take their most useful state at the end (same pattern as piping, which, again, Elixir does backwards), and it fit is so fantastically with Tuple Calls as it basically made this pattern a full callable instead (like anonymous functions)! But that’s gone now *grumble*grumble*

david_ex

david_ex

Someone with more knowledge will hopefully chime in, but here we go:

The work around is using a private function:

defp process_headers(skip_headers \\ true) do
  ...
end

When you declare a function with default params, it is basically syntactic sugar for

defp process_headers(skip_headers) do
  ...
end

defp process_headers(), do: process_headers(true)

This isn’t possible with anonymous functions as they are used “as is”: they can only have one arity (compared to named functions where a new function can be declared by the compiler with the same name, but a different arity).

tallakt

tallakt

This makes sense to me. Some solutions to this would be using a last parameter which was a Keyword, so that:

f = fn args -> 
 # ...
end

f.([]); # call with no args
f.(extra: 10) # call with an argument

Another option is to not use an anonymous function but rather use a MFA triplet (module, function, args). In this case use Kernel.apply/3 to call your “anonymous function”.

https://hexdocs.pm/elixir/Kernel.html#apply/3

Where Next?

Popular in Questions Top

yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

We're in Beta

About us Mission Statement