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

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

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

Mikael Pettersson-5
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?
Reply | Threaded
Open this post in threaded view
|

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

Brujo Benavides-3
This looks like a great rule to add to Elvis.
Would you mind writing an issue for it here?
Or if you're brave enough, we also accept Pull Requests ;)

Cheers!

On Mon, Jan 27, 2020 at 2:22 PM Mikael Pettersson <[hidden email]> 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?


--
Brujo Benavides
about.me/elbrujohalcon
Reply | Threaded
Open this post in threaded view
|

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

Robert Raschke
In reply to this post by Mikael Pettersson-5


On Mon, 27 Jan 2020 13:22 Mikael Pettersson, <[hidden email]> 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

Led
Reply | Threaded
Open this post in threaded view
|

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

Led
In reply to this post by Mikael Pettersson-5

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.


foo(X0) ->
    X = update(X0),
    persist(case predicate(X) of
                true -> another_update(X);
                false -> X
            end).

Start to hate me:)
 
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?


--
Led.
Reply | Threaded
Open this post in threaded view
|

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

Dmitry Belyaev-3
In reply to this post by Robert Raschke
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 <[hidden email]> wrote:


On Mon, 27 Jan 2020 13:22 Mikael Pettersson, <[hidden email]> 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
Reply | Threaded
Open this post in threaded view
|

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

Richard O'Keefe
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 <[hidden email]> 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 <[hidden email]> wrote:
>>
>>
>>
>> On Mon, 27 Jan 2020 13:22 Mikael Pettersson, <[hidden email]> 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