[erlang-questions] Fear and Loathing in Programming La La Land

ok@REDACTED ok@REDACTED
Fri Mar 30 15:15:38 CEST 2012


> "Requiring parameters in the constructor packs much more information
> per line of code than the create-set-call convention, and so I am able
> to read it more quickly."

We've had NFS problems, so I haven't yet seen whoever wrote that.
>
> Yes, IF you know the order of parameters and their meanings in
> advance, OR you have some way to look those up almost instantly. True
> in some cases, not in all. You can read any piece of code very fast.
> The challenge is more often in *understanding* what you're reading. If
> the name of the class/type is suggestive, e.g., Point, then
>
>     P = Point(0,1)
>
> might seem pretty obvious: x=0, y=1.

The issue is that the study in question purports to show that
create-set-call is better, without making it clear that the
only empirical support for that conclusion is limited to a version
of C# before it had optional and keyword parameters.

Smalltalk:
    Point x: 0 y: 1
C# 4 makes it possible to do
    Point(x: 0, y: 1)
Ada:
    Point(X => 0, Y => 1)
SML:
    {x = 0, y = 1} : point
Haskell:
    Point {x = 0, y = 1}

 But how about this?
>
>     R = Rect(a,n0,r,v)

This is what keyword parameters are for:

Lisp:
    (make-rectangle :top y0 :left x0 :bottom y1 :right x1)
Smalltalk:
    Rectangle origin: x0@REDACTED corner: x1@REDACTED
Fortran (yes, really, Fortran!):
    Rectangle(top = y0, left = x0, bottom = y1, right = x1)

Smalltalk has been around since 1980.
Ada has been around since 1981.
Common Lisp was I believe the first OO language to be standardised.
Objective C (a horror movie blend of C and Smalltalk) is from the 80s.
I don't know when DEC Pascal got keyword arguments, but some time
before the mid-80s would be my guess.

>     R = Rect().lowerleft(a,n0).upperright(r,v)

This makes for a much more complex interface.  What if there
_aren't_ any sensible default values?

Erlang doesn't have keyword parameters, but it does have

    rect:new([{bottom,Y1},{left,X0},{top,Y0},{right,X1}])

which gets the job done.  It would be possible to extend
Erlang syntax according to Paul Lyons' "split procedure names"
idea allowing
    rect:new bottom(Y1) left(X0) top(Y0) right(X1)
where the splitting allows the packets after the first to be
freely reordered.  I meant to write up an EEP about that, but
Markdown has defeated me.
>
> " Initialize-everything also reduces the potential for bugs, because
> with create-set-call there become N! ways to initialize an object,
> instead of just 1."
>
> N! overstates the *likely* combinations.

Perhaps.  But it definitely UNDERstands the possible ways.
For 3 facets A B C there are
  A
  A B
  A B C
  A C B
  A C
  B
  B A
  B A C
  B C A
  B C
  C
  C A
  C A B
  C B A
  C B
for fifteen possibilities, not just the 3!=6 you might expect.

The problem is not that all these possibilities *exist* but
that they must all be given *semantics*; create-set-call by
intent cannot and does not *enforce* any 'idiomatic' order or choice.

> Create-all-at-once has its pitfalls too. I mentioned a job where a
> co-worker said that constructors with 22 parameters were OK

In my Smalltalk library, there is just one method with 6 parameters;
   string1 from: begin1 to: end1 sameAs:
   string2 from: begin2 to: end2 ignoringCase: aBoolean.
No genuine method has more.  (ANSI ST allows methods with up to 15
parameters to be called indirectly; there is machinery to allow that.)
Once we had languages where you could return a record, 22 parameters
could no longer be taken seriously.

> I don't think there's any single right way. Readability and
> conciseness will always trade off in complex ways, depending on
> people's preconceptions.

Actually, conciseness just plain doesn't enter into this discussion
at all.  Whether you say
    t = new X(ping: 1, pang: 2, pong: 3, pung: 4)
or
    t = new X(); t.ping(1); t.pang(2); t.pong(3); t.pung(4);
makes very little difference.  In fact C# 3 introduced a wierd
quasi-constructor syntax where you write

    t = new X() {ping = 1, pang = 2, pong = 3, pung = 4}

and what looks just like named parameters is actually compiled
as a series of calls to setter methods.  Given this context, it
is easy to see that

   THERE IS NO SIGNIFICANT DIFFERENCE IN CONCISENESS BETWEEN
   FULL INITIALISATION (using keyword parameters) AND CREATE-
   SET-CALL (using C# 3 weird constructor syntax).

   THERE IS NO SIGNIFICANT DIFFERENCE IN READABILITY BETWEEN
   THE TWO APPROACHES.

What is at issue is
 - exposing incompletely initialised objects
 - the lack of any distinction between 'setters for initialisation
   only' and 'setters that can be called at any time', resulting
   in anything you want to be initialised by name in the
   create-set-call antipattern being vulnerable to revision by
   ANYONE at ANY TIME even in another thread.





More information about the erlang-questions mailing list