# Erlang/OTP 24, Elixir 1.12.3
bmp_signature = <<66, 77>>
#=> "BM"
<<^bmp_signature, _::binary>> = <<66, 77, 30, 0>>
#=> ** (MatchError) no match of right hand side value: <<66, 77, 30, 0>>
Why is this happening?
In short, I'd like to pattern match bitstrings in a cycle, rather than writing method definitions by hand. So instead of this:
@bmp_signature <<66, 77>>
…
def type(<<@bmp_signature, _::binary>>), do: :bmp
…
…something like this:
@signatures %{
"bmp" => <<66, 77>>,
…
}
def detect_type(file) do
find_value_func = fn {extension, signature} ->
case file do
<<^signature, _::binary>> -> extension
_ -> false
end
end
Enum.find_value(@signatures, find_value_func)
end
Is this solvable without metaprogramming?
Your syntax is slightly off. Remember that the pin operator ^
pins only a single value. In your example, you were trying to pin it to 2 values.
So if the thing you are trying to match on is a binary with 2 values that you are aware of, then you would need to pin both of them, e.g.
iex> <<bmp_sig1, bmp_sig2>> = <<66, 77>>
iex> <<^bmp_sig1, ^bmp_sig2, rest::binary>> = <<66, 77, 88, 23, 44, 89>>
<<66, 77, 88, 23, 44, 89>>
iex> rest
<<88, 23, 44, 89>>
The binary syntax <<>>
isn't the only way to do this -- you can accomplish the same with regular strings (assuming the values are in fact strings):
iex> x = "apple"
"apple"
iex> "ap" <> rest = x
"apple"
iex> rest
"ple"
The rub here is that you can't pin a prefix because you need a literal value in order to do the match. This is because the length of the binary isn't known beforehand.
If you know your "signatures" always have 2, 3, or 4 characters, you can code your variables to be pinned appropriately. However, if you must deal with a an unknown length, then you'd probably need to rely on something more like a regular expression or String.starts_with?/2
or similar.