cpackingham

cpackingham

How do I use an arbitrary number of arguments in an anonymous function?
Ex. in JavaScript
…args
Ex. in Python
*args

I need to be able to take any number of arguments in and convert them into a list with an anonymous function.

Showing Posts 1 to 10

peerreynders

peerreynders

Arity is fixed. So you have to pass a list to begin with. Often keyword lists are used.

Examples:

Kernel.spawn/3 - the third argument args is a list of indeterminate length.
Supervisor.start_link/3 where options is a keyword list.

The List module has some special functions like List.keyfind/4 that can be used with keyword lists.

Map.new/1 converts a keyword list to a map - though when a key is duplicated only one key-value is kept.

OvermindDL1

OvermindDL1

To ‘spread’ you have to call apply:

╰─➤  iex
Erlang/OTP 20 [erts-9.1] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.6.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> blah = fn(a, b, c, d) -> a+b+c+d end
#Function<4.99386804/4 in :erl_eval.expr/5>
iex(2)> args = [1,2,3,4]       
[1, 2, 3, 4]
iex(3)> apply(blah, args)
10
iex(4)> defmodule Bloop do def bleep(a, b, c, d), do: a+b+c+d end
{:module, Bloop,
 <<70, 79, 82, 49, 0, 0, 4, 32, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 116,
   0, 0, 0, 13, 12, 69, 108, 105, 120, 105, 114, 46, 66, 108, 111, 111, 112, 8,
   95, 95, 105, 110, 102, 111, 95, 95, 9, ...>>, {:bleep, 4}}
iex(5)> apply(Bloop, :bleep, args)
10
iex(6)> apply(Bloop, :bleep, [0 | args])
** (UndefinedFunctionError) function Bloop.bleep/5 is undefined or private. Did you mean one of:

      * bleep/4

    Bloop.bleep(0, 1, 2, 3, 4)

Do note, this is an indirect call so although still cheap enough, don’t call it in a tight loop where performance is a top necessity, but otherwise it’s perfectly fine to use. :slight_smile:

For that you’ll need a macro. Functions on the BEAM are like functions in C++ or so, they are defined by a name and arity, thus the arity has to match to be called. You can generate many functions that take each count of args and return that, but a macro can do it inline, however you cannot make that anonymous for obvious reasons (ran at compile-time, not run-time). ^.^

To take an arbitrary number of arguments that are not in the ‘arity’ you should pass in a list, or map, or whatever structure is appropriate. :slight_smile:

daveboo

daveboo

I never got an answer that I could use on this, but I’m still very new to Elixir. I was trying to add a sort value to an existing list of maps. I ended up just passing the list down to my client and added it using the javascript function below, but I’d love to know how Elixir handles this?

sortResponders(auction){
return auction.responders.map((r, index) => ({…r, sort: index + 1 }));
}

zkessin

zkessin

The BEAM does not support variable arity functions. So you have to use lists or the like

daveboo

daveboo

Thanks zkessin…I figured I just need to get stronger on lists, maps, etc. The js workaround will do until I get there.

peerreynders

peerreynders

{obj..., a: 1, b: 2, c: 3}

  • If the map already has a :sort atom key you could simply use the %{r | sort: index + 1} update syntax sugar; that syntax can accomodate multiple key updates %{map | a: 1, b: 2, c: 3}

  • If the key may not exist use Map.put/2 - Map.put(map, :b, 2), for multiple keys you can use Map.merge/2 - Map.merge(map,%{a: 1, b: 2, c: 3})

const {a, b, c, ...rest} = obj

iex(1)> map = %{a: 1, b: 2, c: 3, d: 4}
%{a: 1, b: 2, c: 3, d: 4} 
iex(2)> isolate = [:a, :b, :c]
[:a, :b, :c]
iex(3)> {%{a: a, b: b, c: c}, rest} = Map.split(map, isolate)
{%{a: 1, b: 2, c: 3}, %{d: 4}}
iex(4)> IO.puts("#{inspect a} #{inspect b} #{inspect c}  #{inspect rest}")
1 2 3  %{d: 4}
:ok
iex(5)> taken = Map.take(map, isolate)
%{a: 1, b: 2, c: 3}
iex(6)> IO.puts("#{inspect taken}")
%{a: 1, b: 2, c: 3}
:ok
iex(7)> other = Map.delete(map, :a)
%{b: 2, c: 3, d: 4}
iex(8)> IO.puts("#{inspect other}")
%{b: 2, c: 3, d: 4}
:ok
iex(9)> left = Map.drop(map, isolate)
%{d: 4}
iex(10)> IO.puts("#{inspect left}")
%{d: 4}
:ok
iex(11)> 
daveboo

daveboo

Thanks peerreynders, but like I said, I need to go back through my books/courses and work on lists. I’m assuming I would add that inside my Enum.map function to update my list? My sample data structure is as follows (I want dave to have sort: 1, and pete to have sort: 2) :

 iex(1)> r_list = [%{first_name: "dave", last_name: "boo", sort: 9999}, %{first_name: "pete", last_name: "street", sort: 9999}]
pawaclawczyk

pawaclawczyk

In your code {...r, sort: index + 1} is equivalent to Map.put(r, :sort, index + 1).

In Elixir you don’t need the spread operator to make copy, because variables are immutable.

You can use the mentioned construct of %{r | sort: index + 1} if you are sure that key sort exists in r. Map.put will create a key if it does not exist.

However, the outer map of responders.map((r, index) => ({…r, sort: index + 1 })) will not work without a little help. If you use Enum.map you pass a function of single argument into it. It won’t receive the index of item. You must zip the values with indices first i.e. using Enum.with_index, but remember that since now you will have list of two element tuples - {value, index}.

So

responders.map((r, index) => ({…r, sort: index + 1 }))

can be written as

responders
|> Enum.with_index(1)
|> Enum.map(fn {r, index} -> Map.put(r, :sort, index) end)

More info and great examples you can find in Enum documentation.

peerreynders

peerreynders

Enum.map/1 doesn’t supply an index - that is a JavaScript Array thing.

defmodule Demo do
  #
  # version using comprehension
  # https://elixir-lang.org/getting-started/comprehensions.html
  def adjust_sort(list, offset \\ 0),
    do: for({m, index} <- Enum.with_index(list, offset), do: %{m | sort: index})

  #
  # version using recursion
  def adjust_sort2(list, offset \\ 0),
    do: adjust_sort(list, offset, [])

  # base/terminal case. Processed entire list. Reverse it to get original order
  defp adjust_sort([], _, list),
    do: :lists.reverse(list)

  # http://erlang.org/doc/man/lists.html#reverse-1

  # recursive case. Update sort value on this element, recurse on next element
  defp adjust_sort([h | t], index, rest),
    do: adjust_sort(t, index + 1, [%{h | sort: index} | rest])

  #
  # version using reduce/foldl
  def adjust_sort3(list, offset \\ 0) do
    {result, _} = List.foldl(list, {[], offset}, &reducer/2)
    :lists.reverse(result)
  end

  defp reducer(m, {rest, index}),
    do: {[%{m | sort: index} | rest], index + 1}

  #
  # version using Enum.map_reduce
  def adjust_sort4(list, offset \\ 0) do
    {result, _} = Enum.map_reduce(list, offset, &map_reducer/2)
    result
  end

  defp map_reducer(m, index),
    do: {%{m | sort: index}, index + 1}
end

r_list = [
  %{first_name: "dave", last_name: "boo", sort: 9999},
  %{first_name: "pete", last_name: "street", sort: 9999}
]

IO.inspect(Demo.adjust_sort(r_list, 1))
IO.inspect(Demo.adjust_sort2(r_list, 1))
IO.inspect(Demo.adjust_sort3(r_list, 1))
IO.inspect(Demo.adjust_sort4(r_list, 1))
$ elixir demo.exs
[
  %{first_name: "dave", last_name: "boo", sort: 1},
  %{first_name: "pete", last_name: "street", sort: 2}
]
[
  %{first_name: "dave", last_name: "boo", sort: 1},
  %{first_name: "pete", last_name: "street", sort: 2}
]
[
  %{first_name: "dave", last_name: "boo", sort: 1},
  %{first_name: "pete", last_name: "street", sort: 2}
]
[
  %{first_name: "dave", last_name: "boo", sort: 1},
  %{first_name: "pete", last_name: "street", sort: 2}
]
$

I think the Enum.map_reduce/3 version is the closest in spirit for what you are looking for.

daveboo

daveboo

Yes, the documentation led me down the Enum.with_index path, but the tuples tripped me up. I ran into time constraints, and went for the workaround. Thanks, and I hope my newbness helps others reading your suggestions.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews