Good — let’s drill into this.  

At a high level, you’re saying there’s a tension between encapsulation and a 
state-based comparison primitive; that the state-based comparison is a side 
channel through which encapsulated state may be leaked.  That’s true.  (Just as 
there is a tension between “values are objects” and “objects have identity”.)

To pick up on John’s note from v-dev over the weekend, value-objects are more 
easily _forgeable_ than identity-objects.  There are infinitely many possible 
java.lang.Integers, because of the unique-per-instance identity; there are only 
finitely many instances of 

    value class IntWrapper { public int i; }

and, given access to the constructor, you can construct them all, and readily 
stamp out whatever instance you like, and it is just as good as all other 
instances with that state.  

We want value to have as many of the things that classes have, within the 
constraints that values eschew identity.  So they can’t have mutability or 
layout polymorphism.  But they can have methods, fields, constructors, type 
variables, etc.  And we’d like for “encapsulation” to be in this set.  

As a trivial observation, the concern you raise here goes away if the 
constructor is not accessible to the attacker.  That suggests there are at 
least two paths to plugging this leak; tighten state-based comparison, or 
require classes that want to encapsulate their state to also encapsulate the 
constructors that can produce arbitrary state.  

So, rather than blaming ==, or blaming encapsulation, let’s set out some 
expectations for how we want to use encapsulation in values.  

(I think this problem may be related to another problem — that of when a client 
should be allowed to use `withfield`.  For an unencapsulated class like Point, 
where the ctor expresses no constraints, it seems desirable to let clients say 
“p __with x = 2” (with whatever syntax), without making the author expose yet 
more accessor methods, but clearly for encapsulated values, that’s not OK.)  

> On Feb 25, 2019, at 5:11 AM, Remi Forax <fo...@univ-mlv.fr> wrote:
> 
> Hi all,
> there is another issue with making the component wide test available for any 
> value types, it's leaking the implementation.
> 
> Let say we have this class:
> 
> public value class GuessANumber {
>  private final int value;
> 
>  public GuessANumber(int value) {
>    this.value = value;
>  }
> 
>  public enum Response { LOWER, GREATER, FOUND };
> 
>  public Response guess(int guess) {
>    if (value < guess) {
>      return Response.LOWER;
>    }
>    if (value > guess) {
>      return Response.GREATER;
>    }
>    return Response.FOUND;
>  }
> 
>  public static GuessANumber random(int seed) {
>    return new GuessANumber(new Random(seed).nextInt(1024));
>  }
> }
> 
> you can naively think that if we have an an instance of GuessANumber
>  var number = GuessANumber.random(0);
> you have can not get the value of the private field of that instance,
> but using == you can find it because you can use == to test if number is 
> substituable to a user created GuessANumber.
> 
> here is how to find the value without using the method guess()
>  System.out.println(IntStream.range(0, 1024).filter(n -> new GuessANumber(n) 
> == number).findFirst());
> 
> Rémi

Reply via email to