Type Class / Witness

Brian Goetz brian.goetz at oracle.com
Fri Aug 8 20:01:17 UTC 2025


This is a valid observation, but you are getting way ahead of yourself.  My talk this week was about general mechanisms; we are not designing this feature yet, and discussions about it are premature (and likely counterproductive, as people chime in with questions about something that is not yet designed.)

> On Aug 8, 2025, at 12:06 PM, Remi Forax <forax at univ-mlv.fr> wrote:
> 
> Hello,
> for those who where not at the JVMLS, it may make sense to re-read this email later,
> when the talk of Brian on Type class / witness is visible on youtube.
> I write this now because i'm sure i will totally forget about that in few days.
> 
> During the talk, Brian said that witness are restricted to be used only by value classes so we avoid the operator overloading hell that you can see in other languages.
> 
> I think this restriction can be bypassed.
> 
> The problem is that you can always wrap any class into a value class organized as a monad (containing a superposition of states) thus bypassing that restriction.
> 
> 
> As an example, let say we want to abuse of the >> operator to emulate C++ iostream.
> Let suppose we have this witness interface defined in the JDK (it is by example used by Integer)
> 
> witness interface BitwiseNumeric<V> {
>  V rightShift(V v1, V v2);
> 
>  // other methods
> }
> 
> And the witness interface for exact conversion also defined in the JDK
> 
> witness interface Conversion<U, V> {
>  U convert(V value);
> }
> 
> 
> So the trick is to create a value class that encode either a Writer or any value you want to add to the writer as a monad,
> The exact conversion is used to wrap any value into the IOStream, the righShift is used to consume the wrapped value using a side-effect.
> 
> value class IOStream {
> private final Writer writer;
> private final Object o;
> 
> private IOStream(Writer writer, Object o) { this.writer = writer; this.o = o; super(); }
> 
> public IOWriter(Writer! writer) { this(writer, null); }  
> 
> public static final witness BitwiseNumeric<IOStream!> BITWISE_NUMERIC = new BitwiseNumeric<>() {
>   public IOStream! rightShift(IOStream! v1, IOStream! v2) {
>     if (v1.writer == null || v2.writer != null) {
>       throw new ISE();
>     }
>     v1.writer.append("" + v2.o);
>     return v1;
>   }
> 
>   // other methods throw UnsupportedOperationException
> };
> 
> public static final witness Conversion<IOStream!, Integer!> FROM_INTEGER = new Conversion<>() {
>   IOStream! convert(Integer! value) {
>     return new IOStream(null, value);
>   }
> };
> }
> 
> So one can write
>  IOStream stream = new IOStream(writer);
>  var _ = stream >> 2 >> 3;
> 
> I think this is a general mechanism so any operator can be abused that way.
> 
> regards,
> Rémi



More information about the amber-spec-experts mailing list