Oliver

Oliver

One common problem we face in constructing lists is that there is (AFAIK) no support for conditionally inserting members into list declarations.

You can’t write:

allow_even? = false
[
1,
if allow_even? do 2 end,
3
]

because the result would be [1, nil, 3], though syntactically a lot of code would be easier and more readable for us if you could do that.

What if returning a very specific value, like :ignore_list_member would lead to a list being constructed like this:

[1, :ignore_list_member, 3] ==> [1, 3]

Functions could return specifically this value if there’s nothing to insert.

def foo(x), do: if rem(x, 2) != 0 do x else :ignore_list_member end
[
foo(1),
foo(2),
foo(3)
]
===> [1, 3]

Right now we typically do something like piping a whole list like this:

allow_even? = false
[
1,
if allow_even? do 2 end,
3
]
|> Enum.reject(&is_nil/1)

which is fine, but I wonder if this could be solved more conveniently and generic without the need to walk the list a second time after constructing it - just once when it’s “constructed” with the declarative syntax.

If it’s not possible with small effort, I still thank you for reading this. :slightly_smiling_face:

Showing Posts 1 to 10

LostKobrakai

LostKobrakai

Ages ago I did look into mimicing dart lists, which do have an add item conditionally synta as well as spreading loops onto the list:

linusdm

linusdm

I was thinking about this language feature too. As @LostKobrakai mentioned, this is a core feature in Dart. It’s documented here: Collections.

I came across a very nice podcast, where Robert Nystrom explains how this works in Dart, and why they choose to make it part of the language (tldr; because it’s handy to build collections like this, in a context where you’re building UI’s declaratively).

LV side-steps this problem with class lists by ignoring falsey values in these lists. I’m not sure the pull towards this feature is just as strong in Elixir, as it was in Dart. Afaik this can’t be emulated with macros, or something similar, but must be a core language feature.

I’m curious if there were previous discussions about this feature, and if there was consensus in the past about it.

LostKobrakai

LostKobrakai

After I posted a macro implementing those features :smiley: It cannot be done without extra syntax, though elixir won’t be able to do so either. It only has a but more flexibility around new syntax over macros.

Oliver

Oliver OP

That’s an interesting proposal, but looking at the implementation it requires list concatenation underneath if I understand it right.

That would make it very similar to what we’re currently doing:

[1, 2, 3] ++
some_function_returning_a_list() ++ 
[4, 5]

which could also handle if constructs

[1, 2, 3] ++
if x do [:something] else [] end ++ 
[4, 5]

I was kind of hoping that the declarative syntax could be done in such a way at language level that it can avoid paying for concatenation in that way. (Unless ++ concatenation is very efficient. I always resorted to [ x | xs ] syntax where possible assuming ++ comes at a higher cost.)

linusdm

linusdm

Haha, I see where I messed up. I mean without wrapping the list as a whole :sweat_smile: I was thinking of a solution that doesn’t require a certain context to be set up. Maybe only a marcro inside a list.

linusdm

linusdm

Since this is about list literals, I wouldn’t worry too much about performance.

D4no0

D4no0

What is the problem of using macro proposed by @LostKobrakai ?

I’m not a fan of this becoming a language feature, as it involves a lot of mental overload vs not having to write 2 more lines of code.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

If the value is conditionally there then there is no avoiding paying for concatenation. Lists are linked lists, they’re built from back to front. If part of the back is different depending on a runtime value then you have to pay a runtime cost to deal with it.

gregvaughn

gregvaughn

You can use the filter of a for comprehension

iex(9)> require Integer
Integer
iex(10)> allow_even? = false
false
iex(11)> for x <- 1..3, !Integer.is_even(x) || allow_even?, do: x
[1, 3]
iex(12)> allow_even? = true
true
iex(13)> for x <- 1..3, !Integer.is_even(x) || allow_even?, do: x
[1, 2, 3]

or you could use Enum.flat_map but then you’re creating intermediate lists

iex(14)> Enum.flat_map(1..3, fn
...(14)>   x when not Integer.is_even(x) or allow_even? -> [x]
...(14)>   _ -> []
...(14)> end)
[1, 2, 3]
iex(15)> allow_even? = false
false
iex(16)> Enum.flat_map(1..3, fn
...(16)>   x when not Integer.is_even(x) or allow_even? -> [x]
...(16)>   _ -> []
...(16)> end)
[1, 3]
dimitarvp

dimitarvp

On the very rare occasion I’ve done this I just used a list of lists (where the conditional adding of a thing added a non-empty list, and if the condition was false then it simply added an empty list) and then just do List.flatten at the end.

I understand the appeal of having such a language construct but it’s IMO too niche.

Where Next? Top

Trending in Proposals: Ideas Top

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
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
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
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
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews