tommy
Is there a workaround to the inner anonymous function of a closure not being able have a default parameter or clauses with different arities
I’m trying to create a closure that takes in a html tag such as “p” or “h1” and returns an anonymous function that can take a string (to go between the tags) and an optional string of tag attributes such as “style=“color: green;””.
I have tried to do this with an optional parameter and see that this can’t be done and I have also tried clauses with different arities and see this can’t also be done.
Here is my effort with an optional parameter:
defmodule Wrapper do
def html(tag) do
fn
(str, attr \\ "" ) -> "<#{tag} #{attr}>" <> str <> "<#{tag}>"
end
end
end
And here is my effort with two arities in the anonymous function
defmodule Wrapper do
def html(tag) do
fn
(str, attr) -> "<#{tag} #{attr}>" <> str <> "<#{tag}>"
(str) -> "<#{tag}>" <> str <> "<#{tag}>"
end
end
end
And used like:
h1 = Wrapper.html("h1" )
h1.("I'm a green h1 title", "style=\"color: green;\"") |> IO.puts
h1.("I'm a title") |> IO.puts
Is there another way of doing what I need here? I don’t actually need this other than learning.
–
Edited: spelling
Marked As Solved
trisolaran
A simple idea could be to “simulate” different arities with tuples:
defmodule Wrapper do
def html(tag) do
fn
{str, attr} -> "<#{tag} #{attr}>" <> str <> "<#{tag}>"
str -> "<#{tag}>" <> str <> "<#{tag}>"
end
end
end
Wrapper.html("p").("this is a paragraph")
Wrapper.html("p").({"this is a paragraph with style", "style=\"color: green;\""})
Also Liked
eksperimental
When you define a regular function with default values, what are are actually doing is defining more functions with lesser arities.
so:
def x(a, b, c \\ :c),
do: {a, b, c}
what is does in reality is
def x(a, b),
do: x(a, b, :c)
def x(a, b, c),
do: {a, b, c}
So for Elixir x/2 and x/3 are two different functions that just happen to have the same name (well, the compiler does some annotation so you can know they have been defined by same clause).
So if anonymous functions allowed default arguments, what is the variable that they will be assigned to the other functions of lesser arirty?
For the second part, functions can only have a fixed numbers of arities,
so you cannot have have anonymous functions either with more than one arity (you might have seen this in the error thrown by the compiler).
You can make up for this with tuples and keyword lists.
My take builds on the idea of @trisolaran
defmodule Wrapper do
def html(tag) when is_binary(tag) do
fn
(str) when is_binary(str) ->
"<#{tag}>#{str}</#{tag}>"
({str, attr}) when is_list(attr) ->
"<#{tag} #{join_attr(attr)}>" <> str <> "</#{tag}>"
end
end
def join_attr(attr, level \\ 1) do
list =
for {key, value} <- attr do
cond do
is_list(value) ->
"#{key}=\"#{Wrapper.join_attr(value, 2)}\""
level == 1 ->
"#{key}=\"#{value}\""
level == 2 ->
"#{key}: #{value}"
end
end
case level do
1 -> Enum.join(list, " ")
2 -> Enum.join(list, "; ")
end
end
end
In IEx:
iex(44)> h1 = Wrapper.html("h1")
#Function<0.76550479/1 in Wrapper.html/1>
iex(45)> h1.("Wow")
"<h1>Wow</h1>"
iex(46)> h1.({"Title", style: "color: red; font-size: 10em", title: "The title"})
"<h1 style=\"color: red; font-size: 10em\" title=\"The title\">Title</h1>"
iex(47)> h1.({"Title", style: [color: "red", "font-size": "10em"], title: "The title"})
"<h1 style=\"color: red; font-size: 10em\" title=\"The title\">Title</h1>"
derek-zhou
I do not know the answer to your question but I think in Elixir people tend to do more macros than high-order functions. Macros should run faster too.
For example, you can do:
def element(s, tag, text, attrs) when is_binary(text) do
start_tag = "<#{tag}#{attr_string(attrs)}>"
end_tag = "</#{tag}>\n"
[end_tag | text([start_tag | s], text)]
end
[:div, :h1]
|> Enum.each(fn k ->
@doc ~s"""
build non-void element #{to_string(k)}
"""
defmacro unquote(k)(s, inner, attrs \\ []) do
str = to_string(unquote(k))
quote do
element(unquote(s), unquote(str), unquote(inner), unquote(attrs))
end
end
end)
This code is a snippet from html_writer that more or less do what you are trying to do.
tommy
This is fantastic! This will also help me with a logger I’m creating too.
It’s such a clean solution. I’m going to play with that in the morning. Thanks
tommy
This is great! I’m going to copy it into my editor now to play with it. I can see that what you have done here can be used in many different scenarios.
Thank you for helping me.







