You could do:
[ hd(erlang:integer_to_list(Nibble, 16)) || << Nibble:4 >> <= Binary ].
Which would return you a list(string) containing the hex digits of the binary. While I doubt the efficiency of this operation is going to have any effect on the runtime of your system, you could also have this bin_to_hex
function return an iolist which is simpler to construct and will be flattened when output anyway. The following function returns an iolist with the formatting example you gave:
bin_to_hex(Bin) when is_binary(Bin) ->
JoinableLength = byte_size(Bin) - 1,
<< Bytes:JoinableLength/binary, LastNibble1:4, LastNibble2:4 >> = Bin,
[ "<< ",
[ [ erlang:integer_to_list(Nibble1, 16), erlang:integer_to_list(Nibble2, 16), ", " ]
|| << Nibble1:4, Nibble2:4 >> <= Bytes ],
erlang:integer_to_list(LastNibble1, 16),
erlang:integer_to_list(LastNibble2, 16),
" >>" ].
It's a bit ugly, but runs through the binary once and doesn't traverse the output list (otherwise I'd have used string:join to get the interspersed ", " sequences). If this function is not the inner loop of some process (I have a hard time believing this function will be your bottleneck), then you should probably go with some trivially less efficient, but far more obvious code like:
bin_to_hex(Bin) when is_binary(Bin) ->
"<< " ++ string:join([byte_to_hex(B) || << B >> <= Bin ],", ") ++ " >>".
byte_to_hex(<< N1:4, N2:4 >>) ->
[erlang:integer_to_list(N1, 16), erlang:integer_to_list(N2, 16)].