derive4j icon indicating copy to clipboard operation
derive4j copied to clipboard

Ignoring arguments while pattern matching

Open gneuvill opened this issue 7 years ago • 4 comments

It may happen that value constructors of an ADT take, say, five or more arguments. The pattern matching clause generated by derive4j can then become a bit ugly (necessitating a huge lambda expression) especially when you only need a few of all the arguments.

Would it be possible to generate "matchers" that would accept "tupled" lambdas ? (see the following example)

@Data(flavour = Flavour.FJ)
static abstract class Staff { // a dumb type
    Staff() {}

    interface Cases<R> {
        R Common(String name1, String name2, String name3, String name4, Integer age, String adress);
        R Admin(String name1, String name2, String name3, String name4, Integer age, String adress, String role);
    }

    abstract <R> R match(Cases<R> cases);
}

// current state of affairs
IO<Unit> test(Staff staff) {
    return Staffs.caseOf(staff)
        .Common((name1, name2, name3, name4, age, adress) -> IOFunctions.stdoutPrint(name3))
            
        .Admin((name1, name2, name3, name4, age, adress, role) -> IOFunctions.stdoutPrint(role));
}

// proposition
IO<Unit> tupledLambda(Staff staff) {
    return Staffs.caseOf(staff)
        // alternative name to preserve type inference (not sure it would be necessary)
        ._Common(__ -> IOFunctions.stdoutPrint(__._3())) // '__' would be of type P6<String, String, String, String, Integer, String>
            
        ._Admin(__ -> IOFunctions.stdoutPrint(__._7())); // '__' would be of type P7<...>
}

What do you think ?

gneuvill avatar May 17 '17 14:05 gneuvill

Looks like String name1, String name2, String name3, String name4, Integer age, String adress should go into its own product type, no?

jbgi avatar May 17 '17 14:05 jbgi

Well in that case yes, but it was just an example ; creating yet another product type is not always possible or desirable. Anyway, my point is just one of syntactic convenience : sometimes having your type completely de-structured is what you want ; sometimes being able to pick just the data you need is handier.

I guess we could also see this "auto-tupling" feature as a kind of poor-man's as-pattern...

gneuvill avatar May 17 '17 17:05 gneuvill

I think I got an idea for this:

@Data(flavour = Flavour.FJ)
public static abstract class Staff { // a dumb type
    Staff() {}

    interface Cases<R> {
        R Common(String name1, String name2, String name3, String name4, Integer age, String adress);
        R Admin(String name1, String name2, String name3, String name4, Integer age, String adress, String role);
    }

    public abstract <R> R match(Cases<R> cases);

    public abstract Staffs.Evaluated eval();
}

would generate in Staffs.java:

   // will use `sealed` semantic once supported by jdk:
  public static abstract class Evaluated extends Staff {
    Evaluated(){}
     // generated only when targetting jdk that do not support pattern matching:
    public abstract R match(F<Common, R> common, F<Admin, R> admin);
  }

 public static final class Common extends Evaluated {
  public final String name1;
  public final String name2;
  ....
   public R match(F<Common, R> common, F<Admin, R> admin) {
       return common.f(this);
   }
}
....

This should also ease support for built-in pattern matching when it comes, while preserving the lazy constructor functionality.

WDYT?

jbgi avatar Mar 22 '18 11:03 jbgi

Seems clever, as usual... Do you think having Staff 's subtypes public could mess with type inference (as I think was/is the case in scala ?) ?

gneuvill avatar Mar 22 '18 21:03 gneuvill