As funções para lidar com entrada e saída são fornecidas pelo módulo IO.
IO.puts/2 escreve uma string na saída padrão, seguida de uma quebra de linha. Retorna :ok.IO.write/2 escreve uma string na saída padrão, sem adicionar uma quebra de linha. Retorna :ok.IO.gets/2 lê uma linha da entrada padrão.IO.inspect/2 escreve qualquer coisa na saída padrão e aceita opções úteis, como :label. Retorna o valor recebido sem alterações. Útil para fazer debug.String.Chars
Se você tentar passar para IO.puts/2 um valor que ele não consegue imprimir, vai ver um erro Protocol.UndefinedError.
IO.puts({:ok, 7})
# ** (Protocol.UndefinedError) protocol String.Chars not implemented for {:ok, 7} of type Tuple
Quando isso acontecer, talvez você queira usar IO.inspect/2 no lugar.
IO.inspect/2 para fazer debugIO.inspect/2 é perfeito para fazer debug porque retorna o argumento recebido sem alterações, consegue imprimir qualquer coisa e oferece uma opção :label.
"HELLO\n"
|> String.trim()
|> IO.inspect(label: "step 1")
|> String.downcase()
|> IO.inspect(label: "step 2")
# > step 1: "HELLO"
# > step 2: "hello"
# => "hello"
O primeiro argumento de todas essas funções é um dispositivo de IO, cujo valor padrão é :stdio (entrada/saída padrão). Para escrever no dispositivo de erro padrão, use :stderr. Um dispositivo de IO também pode ser um processo, por exemplo um criado ao chamar File.open/2, o que permite escrever em um arquivo.