I have a case where I wish to apply modifications to an object based on the presence of (a few, say, 5 to 10) optionals. So basically, if I were to do it imperatively, what I'm aiming for is :
var myObject = ...
if (option.isDefined) {
    myObject = myObject.modify(option.get)   
}
if (option2.isDefined) {
    myObject = myObject.someOtherModification(option2.get)
}
(Please note : maybe my object is mutable, maybe not, that is not the point here.)
I thought it'd look nicer if I tried to implement a fluent way of writing this, such as (pseudo code...) :
myObject.optionally(option, _.modify(_))
        .optionally(option2, _.someOtherModification(_))
So I started with a sample code, which intelliJ does not highlight as an error, but that actually does not build.
class MyObject(content: String) {
   /** Apply a transformation if the optional is present */
  def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = 
      optional.map(operation(_, this)).getOrElse(this)
   /** Some possible transformation */
  def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
object Test {
  val my = new MyObject("test")
  val option = Option(2)
  my.optionally(option, (size, value) => value.resized(size))
}
Now, in my case, the MyObject type is of some external API, so I created an implicit conversion to help, so what it really does look like :
// Out of my control
class MyObject(content: String) {
  def resized(length : Int): MyObject = new MyObject(content.substring(0, length))
}
// What I did : create a rich type over MyObject
class MyRichObject(myObject: MyObject) {
  def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = optional.map(operation(_, myObject)).getOrElse(myObject)
}
// And an implicit conversion
object MyRichObject {
  implicit def apply(myObject: MyObject): MyRichObject = new MyRichObject(myObject)
} 
And then, I use it this way :
object Test {
  val my = new MyObject("test")
  val option = Option(2)
  import MyRichObject._
  my.optionally(option, (size, value) => value.resized(size))
}
And this time, it fails in IntelliJ and while compiling because the type of the Option is unknown : 
Error:(8, 26) missing parameter type
  my.optionally(option, (size, value) => value.resized(size)) 
To make it work, I can :
- Actively specify a type of the sizeargument :my.optionally(option, (size: Int, value) => value.resized(size))
- Rewrite the optionallyto a curried-version
None of them is really bad, but if I may ask :
- Is there a reason that a curried version works, but a multi argument version seems to fail to infer the parametrized type,
- Could it be written in a way that works without specifying the actual types
- and as a bonus (although this might be opinion based), how would you write it (some sort of foldLefton a sequence of optionals come to my mind...) ?
 
     
    