Tom Nicklin
Posted on July 29, 2022
Outside of mix.exs
I haven't engaged with .exs
files much. I imagine that might be the same for some of you. However, I ran into a situation recently where I needed to.
I wanted my elixir project in a docker container, so that when running it would go straight into the terminal awaiting user input; collect the input line by line and return a list of all of those lines when an empty line is given. Creating an elixir script as the ENTRYPOINT
for the program felt like the right thing to do. However, I struggled to find any substantial documentation and/or examples to understand the limitations of what can and cannot be done with elixir script.
After some trial and error, I found some bits that worked and started iterating from there until I had something I could use for my project. What follows are examples of where the code does the same thing but in different formats and layouts. Side note, for code snippets from the terminal, I've named the file start.exs
Also, we get to have a look at IO.stream
(docs) for anyone else that might have ran into the same issue as me.
You can define functions!
In hindsight (which is always 20/20) this makes sense but for whatever reason it didn't occur to me that you can do this. The only difference here compared to a normal elixir module is that you call the function at the end of the file.
defmodule Lines do
def run(lines \\ []) do
case IO.read(:stdio, :line) do
"\n" ->
# done reading lines!
lines
|> Enum.reverse()
|> Enum.map(&String.replace(&1, "\n", ""))
|> IO.inspect()
line ->
run([line | lines])
end
end
end
Lines.run()
$ elixir start.exs
hi
there
["hi", "there"]
No thanks, I prefer my functions anonymous.
Here's another approach, if for whatever reason you didn't want to define modules or functions.
IO.stream(:stdio, :line)
|> Enum.reduce_while([], fn line, lines ->
case line do
"\n" -> {:halt, lines |> Enum.reverse()}
line -> {:cont, [line | lines]}
end
end)
|> Enum.map(&String.replace(&1, "\n", ""))
|> IO.inspect()
$ elixir start.exs
hi
there
["hi", "there"]
Time for my final form
IO.stream(:stdio, :line)
|> Enum.take_while(&(&1 != "\n"))
|> Enum.map(&String.replace(&1, "\n", ""))
|> IO.inspect()
$ elixir start.exs
hi
there
["hi", "there"]
Conclusion
The point of this article, wasn't so much the code but I think I struggled to get my head around scoping issues after only ever dealing with traditional elixir code. Hopefully, if you're in a similar situation, you can now see that there is more flexibility than you might have originally thought.
In regards to IO.stream
docs. I made a PR to make some amendments.
Posted on July 29, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.