Lesser-known Erlang tips and tricks?

Following on from the Do you make use of term_to_binary? thread, do you know of any other lesser known tips or tricks you think others might be interested in?

If so please share! :052:

3 Likes

There’s a compact way to do a one-line conditional (ab)using list comprehensions:

[do_thing() || BooleanValue, check_stuff()]

It works because you can have both list generators and filters, but the generators are optional.

It even works with conditions not allowed in guards. But be aware of readability concerns :wink:

7 Likes

Oh, here’s another one: there’s a built-in function in the shell—rp(Term)—that pretty-prints terms to infinite depth. That way you don’t have to type io:format(“~p~n”, [Term]) all the time! :grin:

8 Likes

If you want to learn more about this, you can check the article(s) I wrote a while back…

As a matter of fact, the whole Erlang Battleground blog is full of articles that would be proper responses to the topic of this thread.

7 Likes

In Erlang/OTP 26, we have documented some previously undocumented details about list comprehensions, including what happens when filters fail:

(to be included in the second release candidate, which will be released this week)

11 Likes

The right-hand operands of andalso and orelse can be any term, it doesn’t have to be true or false.

As a (contrived) example, if maps:get/3 didn’t exist, it could be written like this:

maps_get_with_default(Key, Map, Default) ->
    case maps:is_key(Key, Map) andalso {value, maps:get(Key, Map)} of
        false -> Default;
        {value, Value} -> Value
    end.
8 Likes

I got the feeling that I’ll be doing this a lot in this thread. When you folks get tired of this, let me know… :roll_eyes:

But… I also wrote an article about that semantically questionable technique…

2 Likes

I was a bit reluctant to share this “trick”, TBH, and I’m definitely not encouraging it :smile:

Maybe all posts in this thread should flash a warning like “Don’t do this at home!:sweat_smile:
Those lesser know tricks are lesser known not so much because they are secret magic but because they are dirty tricks :wink:

3 Likes

IMHO:

  1. “The right-hand operands of andalso and orelse” is not a trick.
  2. “List incomrehentions” is a needless boasting.
bc(B) when byte_size(B) > 64 -> binary:copy(B);
bc(B) -> B.

I use this to store values is process state when such values are received from an external large BLOB (like a JSON) after jsone:decode/1.

3 Likes

I like to write it that way, Mainly to avoid multiple case
I also defined some macros

Blockquote
-define(IF(IFTure, DoThat), (IFTure) andalso (DoThat)).
-define(IIF(Cond, Then, That), case Cond of true → Then; _ → That end).
-define(IIF(Expr, Expect, Then, ExprRet, That), case Expr of Expect → Then; ExprRet → That end).

this is just to make the code look like it’s on one line

1 Like

That is why I put “trick” in quotes :wink:

1 Like

Using ets:match_spec_run/2 over lists for dynamic filtering at runtime.

Meant I could avoid erl_parse/erl_eval/runtime-module-compilation.

I vaguely remember it being ‘fast enough’ for a small-time advertising auction hot path when I last used it (5+ years ago), no idea if it still is the best option. The need here was to take the request, turn it into a match_spec and run it over targeting rules.

On a related note, merl was helpful for my RADIUS encoder/decoder. I parsed the dictionaries and emitted code directly…in a similar vain to using ASN.1 but for RADIUS.

4 Likes

I use it in pge_ps.
And no, I didn’t come up with it myself, but I spied on gproc_ps.

1 Like

(Disclaimer: This is not a trick and definitely not a tip. Do this only at home, not in public :wink:)


A while ago, @juhlig told me about a presentation from a conference he attended, where somebody created a fun project for an obfuscation library that turns Clojure code into Morse code which is still valid Clojure code, and I was like “Haha, yeah, whatever serves to waste your time :sweat_smile:”.

But just lately, I realized what fun stuff you can do, also in Erlang.
The below is valid, working Erlang code :crazy_face: (sane versions in comments):

-module(hugger).

-export(['🤗'/1]).
% -export([hug/1]).

'🤗'('😢') -> '🙁';
% hug(crying_person) -> sad_person;

'🤗'('🙁') -> '😐';
% hug(sad_person) -> ok_person;

'🤗'('😐') -> '🙂';
% hug(ok_person) -> happy_person;

'🤗'(Other) -> Other.
% hug(Other) -> Other.

(The module name must not contain non-latin1 characters though, so we can’t go all the way and use Emojis there. Bummer… :woman_shrugging:)

Trying it out in the shell:

1> hugger:'🤗'('😢').
'🙁'
2> hugger:'🤗'('🙁').
'😐'
3> hugger:'🤗'('😐').
'🙂'
4> hugger:'🤗'('🌳').
'🌳'
5> lists:map(fun hugger:'🤗'/1, ['😢', '🙁', '😐', '🙂', '🌳']).
['🙁','😐','🙂','🙂','🌳']
5 Likes

You. Are. Evil. :flushed:
Much as I like you, the day I see something like this used, I swear I’ll sue you for putting crazy ideas in peoples heads :crazy_face:

That said, this was the presentation I attended, last year at ClojureD/ClojureU: :clojureU 2022: "Use of Macros Leads to Remorse" by Paula Gearon - YouTube

Something like this can be done out of the box in Clojure as well btw:

user=> (defn 🤗
  [x]
  (case x
    :😢 :🙁,
    :🙁 :😐,
    :😐 :🙂,
    x))
#'user/🤗

user=> (🤗 :😢)
:🙁

user=> (map 🤗 [:😢 :🙁 :😐 :🙂 :🌳])
(:🙁 :😐 :🙂 :🙂 :🌳)
2 Likes

Sometimes i find block expressions useful especially with list comprehensions

[ begin 
    do_something(X), 
    do_something_else(X),
   ...
end || X <- ...]
2 Likes

This sounds somewhat familiar to me… I wonder why that may be? :thinking:

3 Likes

You can implement a rather efficient single-shot “pub/sub” via monitors, and pass data in a ‘DOWN’ messages:

  1. Create a process A
  2. Have other processes monitor A
  3. When some condition occurs, A terminates with exit reason = {insert arbitrary data}
  4. Other processes get notified by BEAM VM without running any Erlang code.
1 Like

In this vain, plenty of tricks in:

One of the really interesting tricks to come of it was to raise the process priority of the dispatcher.

2 Likes