Was trying to pattern-match on a binary but was getting unexpected(for me, at least) behaviour.
# this works
"foo" <> _rest = "foobar"
t = "foo"
# and this works
^t = "foo"
# but this doesn't
^t <> _rest = "foobar"
# this doesn't work too
t <> _rest = "foobar"
For the ones that don't work, I get this error: a binary field without size is only allowed at the end of a binary pattern.
Is this a bug or is this just not possible to support?
It is not possible to support because we need to know the size before hand. It is the same with lists.
Interestingly this works
<
@2kodes that is because the left hand side is a literal, and so the byte size is known.
Too bad this wasn't possible. Oh well, just a really minor thing though.
How would you go about accomplishing something like this though?
Is there any way to do it then ? (I'm new to elixir so I could have missed something obvious)...
I'm having a list of string which all go like this :
"John Doe (absent)"
"Chuck Norris (absent)"
"Wladimir Putin (absent)"
Is there a way to easily fit the Users name into a variable (since user <> "(absent)" = string) can't work...)?
You can use a regex
[_, name] = Regex.run(~r/([\w\s]+) \(absent\)/, str)
String.split/1
["(absent)" | tail] = str |> String.split() |> Enum.reverse()
name = tail |> Enum.reverse() |> Enum.join(" ")
or something gnarlier...
")tnesba( " <> name = str |> String.reverse()
name = name |> String.reverse()
string = "John Doe (absent)"
size = byte_size(string) - byte_size(" (absent)")
<<name::binary-size(size), " (absent)">> = string
name
#=> "John Doe"
You can still pattern match, but you need to explicitly compute the size.
@uri I like that "gnarlier" method but the regex one seems more obvious.
@michalmuskala That seems to be the cleanest method to me.
For my part, I've done it like this, which is kinda stupid but it works :
string = "John Doe (absent)"
username = string |> String.replace(" (absent)", "")
@ratouney replace suffix may even be a better call in your case:
String.replace_suffix("Foo (absent)", " (absent)", "")
Oh yeah, if it's just about removing the suffix then replace_suffix works the best. The pattern matching though has slightly different semantics in that it will fail if the suffix is not there, unlike replace or replace_suffix.
I still don't understand why ^t <> _rest = "foobar" doesn't compile.
The equivalent code in Erlang is fine:
Root = <<“blaaa”>>
L = [...]
lists:map(fun (<<Root/binary, URL/binary>>) -> URL end, L)
@fenollp it doesn't work for me here (Erlang/OTP 21), neither in the shell nor in a compiled module:
1> Root = <<"blaaa">>,
1> L = [1, 2, 3],
1> lists:map(fun(<<Root/binary, URL/binary>>) -> URL end, L).
* 3: a binary field without size is only allowed at the end of a binary pattern
Woops! You're totally right and I apologize for my tone.
%% This works
Size = byte_size(Root)
lists:map(fun(<<Root:Size/binary, URL/binary>>) -> URL end, L)
Now couldn't the ^ be taken into account in
^t <> _rest = "foobar"
so the compiler could rewrite it to:
<<^t :: size(bit_size(t)), _rest>> = "foobar"
This would still not work in function heads (except with @consts) thus be a bit awkward sometimes, but would be great most of the time IMO.
What do you think?
I think I remember having that same discussion on the Erlang ML earlier this year too :)
The problem is that. <<^foo, bar::binary>> is already valid today and it assumes that it is an integer:
iex(1)> foo = 1
1
iex(2)> <<^foo>> = <<1>>
<<1>>
iex(3)> <<^foo, bar::binary>> = <<1>>
<<1>>
iex(4)> bar
""
The compiler could automatically add the size if the binary modifier is there but if you already added the binary, you might as well add the size.
Alternatively we could make it work for <> especially but it may blurry the line a bit too much. Especially because <> is meant to mirror ++ and mirror does not allow a runtime list prefix either (for different reasons).
It's possible we might get new binary matching instructions in OTP 22 that might allow some new things in an efficient way.
Most helpful comment
You can still pattern match, but you need to explicitly compute the size.