linter/checker for single-threaded state via numbered variables?

Richard O'Keefe raoknz@REDACTED
Tue Jan 28 05:56:57 CET 2020


The S0 S1 ... Sn S convention ultimately derives from Prolog,
where it was used for (ordered) positions in a sequence or
for (ordered) states.  In both cases, the variables had exactly
the same (design-level) type.  Common practice was to use
grammar rules to suppress these variables entirely.  (Just as
common Haskell practice is to define and use combinators
to hide "plumbing".)

One option would be to use inlined higher-order functions to
hide "plumbing" variables.  Another option is to break functions
where you have many such variables into smaller pieces.

foo(X) ->
    persist(maybe_transform(update(X))).

transform(X) ->
   case predicate(X)
       of true -> another_update(X)
         ; false -> X
   end.

In F# you'd write something like
fun foo x -> x |> update |> maybe_update |> persist;;
although I feel that F# over-uses this idiom.

One thing I'll say about the numbered variable approach is that you had
better get your code structure right first time, because it is a pain to
introduce extra steps, remove steps, or reorder steps.

It can get quite hairy when you entangle multiple states of *two*
"variables".  Imperative updates make this *look* straightforward,
but the tangled mess is still there.

On Tue, 28 Jan 2020 at 14:15, Dmitry Belyaev <be.dmitry@REDACTED> wrote:
>
> That's definitely an option, however name generation may be difficult in some cases.
> The one I always have problems with is filling cowboy response by calling a cascade of functions updating request structure progressively. I'm not sure if other developers prefer typing ReqWithHeaders, ReqWithContentType, ReqWithBody, ReqHalted to consecutive but otherwise meaningless numbers.
>
> On 28 January 2020 9:04:38 am AEDT, Robert Raschke <rtrlists@REDACTED> wrote:
>>
>>
>>
>> On Mon, 27 Jan 2020 13:22 Mikael Pettersson, <mikpelinux@REDACTED> wrote:
>>>
>>> Since Erlang doesn't allow you to rebind a variable in a scope where it's already bound (that becomes a match instead), there is a convention of using numbered variables to denote successive "generations" of some object or state that's intended to be used in a single-threaded or linear way.  For example:
>>>
>>> foo(X0) ->
>>>   X1 = update(X0),
>>>   case predicate(X1) of
>>>     true ->
>>>       X2 = another_update(X1),
>>>       persist(X2);
>>>     false ->
>>>      persist(X1)
>>>   end.
>>>
>>> Here, whenever X(N+1) becomes bound, X(N) should become unused, and it should be an error if X(N) did not become unused.  A variation is that the "final" value is the one without a number suffix.
>>>
>>> Is there any Erlang linter or style checker that can perform these types of checks?
>>
>>
>> For what it's worth, I consider this practice a bad code smell. Using numbered names like that is lazy. Usually such code can be radically improved upon by using names that properly describe the values they represent. Very often doing this will start you on the path to simpler code that ends up not requiring this kind of incremental buildup of "stuff".
>>
>> Cheers,
>> Robby
>>
>
> --
> Kind regards,
> Dmitry Belyaev



More information about the erlang-questions mailing list