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

Jan Burse janburse@REDACTED
Wed Apr 4 23:24:04 CEST 2012


Richard O'Keefe schrieb:
> Continuing with the file stream example, it*might*  make sense for
>
>      r := FileReader new.
>      r read: 'foo/bar.txt'.
>
> to work by initially connecting r to /dev/null and having #read: be
> like C's freopen(): close the current connection and establish a new
> one.  That would be default-but-FULL-initialisation, not partial
> initialisation.

It is possible to make even more pathological examples.
Assume we have a dictionary, where we place key-value
pairs. Further assume that the dictionary uses the hash
function of the key to organize its data.

The dictionary can be imparied by using a setter on
a key after a key-value pair has been put into the
dictionary. A hash based dictionary is for example the
HashMap class in Java. So assume our keys are:

      class myKey {
         int alfa;
         int beta;

         int hashCode() {
            return alfa*31+beta;
         }

         void setAlfa(int a) {
            alfa=a;
         }

         void setBeta(int b) {
            beta=b;
         }

         /* getter */
      }

Then what result should the following code give?

     myKey mykey = new myKey();
     mykey.setAlfa(123);
     mykey.setBeta(456);
     HashMap map=new HashMap();
     map.put(mykey,"Hello World!");
     mykey.setBeta(789);
     Systen.out.println("map.get(mykey)="+map.get(mykey));

Actually it turns out that the HashMap is not that
impaird, since in JDK 1.7 (maybe already in previous
versions), the hash is computed once and then stored
inside the dictionary. The Entry has a separate
hash field:

     class Entry {
         int hash;    <<< here
         Object key;
         Object value;
         Entry next;

         ...
     }


But generally there are two dimensions that now
determine how the setter/getters should be designed
of an object. We have already seen the synchronized/
unsynchronized dimension. When an object does not
leak a thread, no synchronization is needed.

An additional dimension is the muttable/immutable
category. Sometimes it can be desirable to make
an object immutable. The easiest to make an object
immutable is to not provide any setters. So basically
we have the following matrice:

             +---------------------------------+
             | Sync          | Unsync          |
+-----------+---------------+-----------------+
| Mutable   | Sync Getter   | Getter          |
|           | Sync Setter   | Setter          |
+-----------+---------------+-----------------+
| Immutable | Sync Getter   | Getter          |
|           | No Setter     | No Setter       |
+-----------+---------------------------------+

But how does an immutable object get its attribute
values when it doesn't have setters? Well one
solution is to have a (public) constructor. So we
could provide a class that corresponds to the above
myKey class, but that is immutable:

     class myKeyImmutableByConstructor {
         int alfa;
         int beta;

         myKeyImmutableByConstructor(int a, int b) {
            alfa = a;
            beta = b;
         }

         /* getter */
     }

An other approach is to have both a mutable object
class and an immutable object class. And then
create the immutable object from the mutable object:

     class myKeyImmutableByCopy {
         int alfa;
         int beta;

         myKeyImmutableByCopy(myKey k) {
            alfa = k.getAlfa();
            beta = k.getBeta();
         }

         /* getter */
     }

There are a lot of variations to the above them.
But bottom line is that in Java there are a lot
of immutable classes or semi immutable classes
on purpose.

A wide class of immutable classes are value
objects. Classes such as Complex that bundle
a pair of reals come to mind. But the Java runtime
system has already a couple of value objects:

     String
     BigInteger
     BigDecimal
     File
     URL

The above are all immutable and don't have
setters. Microsoft should know about immutables.
C# has the readonly keyword. And in F# fields
are by default immutable, one needs the mutable
keyword for the reverse.

In Java the immutable stuff works since it has
information hinding. There are the modifiers
public, private, etc.. If a field is private
it cannot be accessed from the outside, putting
aside special reflection which overrides modifiers
and native code.

Now the question is why does the paper "Usability
Implications of Requiring Parameter in Objects'
Constructors" not address the idea of immutable
objects. Eventually there is a problem when
they want to cover C++. On wikipedia one can
read:

     Notice that in C++ it is not necessary —
     and in fact impossible — to provide a
     specialized constructor for const instances.

I am not an expert for C++, but when the above
is true, then it is clear why setters are needed
and everything goes havoc. For example Java
doesn't have the above restriction, an immutable
object can have multiple constructors:

     class myKeyImmutableByConstructor {
         int alfa;
         int beta;

         myKeyImmutableByConstructor(int a, int b) {
            alfa = a;
            beta = b;
         }

         myKeyImmutableByConstructor() {
            alfa = 0;
            beta = 0;
         }
     }

The above solves the problem of convenience and
evolvability:

- Convenience: Shorter constructors can be
   defined and co-exist for convenience.

- Evolvability: Newer longer constructores can be
   defined and co-exist for evolvability. For
   example to cover late requirements or simply
   new attributes that are added over the time.

So I guess it is invalid when the study says:

"Based on a study of 30 programmers of three
different personas, we have found that APIs
that required constructor parameters did not
prevent errors as expected and that APIs that
instead used the create- set-call pattern of
object construction were more usable."

In the sense that their results are not applicable
to Java, since this would mean comparing apples
with oranges. And in general that their results
make a too broad generalization from MS APIs
to APIs in general. At least if one is not careful
in reading the paper, one might get the impression
that something is said about APIs in general.
But such a study woud require a broader testing
of different API approaches.

Bye



More information about the erlang-questions mailing list