A 2:1 packer

For chars scoring, it can reduce code by a factor of two.

Spoiler

Decompressor

Note: the overhead is 29 characters so your uncompressed code must be at least 60 characters to get a benefit.

Code.eval_string<<"..."::utf16>>

Compressor (in Python)

Note: the code size must be divisible by 2 for the compressor to work. If not, adding a trailing space should suffice.

print('CODE'.encode().decode('utf-16be'))

Try it online!

Integer Constants

The ? operator gets the unicode codepoint for a character. So, integer literals between 100 and 1114111 can be replaced by a ? followed by the character the unicode codepoint represents to save characters and sometimes bytes as well.

Conditionals

It is always shorter to use the boolean operators && and || instead of if and else.

Recursive Functions

The usual way to define a recursive function is:

defmodule,M do: def f(x),do: x<1&&0||x+f(x-1)

However, by making the function a parameter to itself this can be rewritten as an anonymous function like this:

f=fn f,x->x<1&&0||x+f.(f,x-1)end

Or if you use the shorter syntax for anonymous functions you can write it like this:

f=& &2<1&&0||&2+&1.(&1,&2-1)

Modules

Modules can be imported to use function names without the corresponding module name. This saves bytes with 3 or more uses of Enum, List, or String. Also, modules are symbols so in very specific circumstances this can be helpful. If you use exactly two functions from the String module, the binding s=String saves one byte.

Looping

Using list comprehensions is sometimes shorter than mapping over a range. Also, remember all of the features comprehensions provide. You can do nested loops, bind variables, and use filters.

Enum.map 0..99,& &1>5&&IO.puts&1
for n<-0..99,n>5,do: IO.puts n

Mapping has an advantage if you are already importing Enum, though you have the disadvantage of not being able to nest function comprehensions using &. List also return a list of values, so they can be used within other maps as well.

Enum.map 0..99,&Enum.sum(for(n<-0..&1,do: f.(&1)))

Since Elixir doesn't allow mutable variables, you can abuse scan and reduce in order to pass a changing value in a loop.

Enum.scan 0..9,0,fn _,n->IO.puts n
f.(n)
end

Multiple "variables" may need a tuple or list structure to be passed along e.g. {x,y} or [x|y]. You can also leave out the initial value and the scan will start with the first value of the enumerable. List comprehensions also have a reduce: option, though it is quite verbose.

Erlang

Elixir has access to all of Erlang's functions (documented here), some of which do not have equivalent functions in Elixir's standard library. These are accessible through symbols. Some good modules are:

Charlists

Use charlists over strings when appropriate. List.duplicate is shorter than String.duplicate for example. Both display the same when printed using IO.puts. While to_charlist is available in the Kernel and works on most things, it is usually shorter (though slower) to interpolate into single quotes, e.g. '#{99}'. Symbols are printed and interpolated as normal, so may save a byte over a normal string, ('a' vs :a)

Function Call Syntax

Remember all of the ways you can call functions.

f(&1,g(&2,&3))

vs.

f&1,&2|>g&3

Blocks

Blocks can be created in parenthesis and execute multiple statements returning the last value in the block. This is very useful for executing side effects in anonymous functions or assigning variables (though they are only within the scope of the block). Note that you can't convert a block on its own to an anonymous function. You can also do this within a string interpolation ('#{&1;&2}')

Enum.map 0..9,&IO.puts(&1)&&2*&1   # Good
Enum.map 0..9,&(IO.puts&1;2)*&1   # Good
Enum.map 0..9,&(IO.puts&1;2*&1)   # Error