eclipse-archived / ceylon

The Ceylon compiler, language module, and command line tools
http://ceylon-lang.org
Apache License 2.0
399 stars 62 forks source link

Add `Object.mapObject` method to match `java.util.Optional.map()` #7432

Open xkr47 opened 6 years ago

xkr47 commented 6 years ago

I would like to propose the addition of a new method to the ceylon.language.Object class:

  Result mapObject<Result,Element>(Result mapping(Element element)) {
    assert(is Element this);
    return mapping(this);
  }

This would match the java.util.Optional.map(...) method so you could use it like:

String? input = ...;
String? result = input?.mapObject((i) => i + i);

The return type is intentionally left optional.

If we had a This type referring to the class of the current instance, we could add a given Element satisfies This clause.

xkr47 commented 5 years ago

And now they added this transform() method to String in Java12... https://blog.codefx.org/java/java-12-guide/#Indenting-and-Transforming-Strings

Why didn't they just put it in Object while at it`..

Voiteh commented 5 years ago

I would suggest this feature a bit differently but overall, adding some map/convert/transform method to Object seems grate to me. Providing some nice hook, to use some framework to required transformations could be possible if mapObject method, would be more generic and gives possibility to provide some additional arguments. For some cases, conversions requires more dependencies. For other it requires to delegate part of the task to other converters.

Taking those in consideration, I would rather call mapObject, convert or transform. I would also propose a Conversion interface rather than method reference but I guess it is just my try to categorise everything ;) .


shared interface Conversion<Element,Result,Arguments> given Arguments satisfies Anything[] {

    throws(`class Exception`)
    shared formal Result convert(Element element,Arguments arguments);
}

And method in Object or maybe in Anything not sure though here, would look like

Result|Exception convert<Result,Arguments>(Conversion<This,Result,Arguments> conversion, Arguments arguments) given Argument satisfies Anything[] {
    try{
      return conversion(this,arguments));
    }catch(Exception x){
        return x;
    }
}

The convert method in Conversion interface throws an exception rather than returning, because it simplifies the flow, when delegating part of conversion to other Conversion's, so there is not need to check, wheatear nested converter thrown. Rather every-time when there would be delegation:


...
Integer|Exception result=delegator.convert(something,`Integer`);
if(is Integer result){
    .... something more
}
else{
    return result;
}

We would have

....
Integer result=delegator.convert(something,`Integer`);
something more...

It would land in Object.convert method anyway, and be returned to client to force handling of exception.