I am against this proposal.
It would make call-sites behave weirdly:
def foo[T](x: Option[T]) = x
val x = 4
foo(x) // Some(4)
foo(Some(x)) // Some(4)
// They return the same value
val y = Some(4)
foo(y) // Some(4)
foo(Some(y)) // Some(Some(4))
// They don't return the same value
def forwarder[T](x: T) = foo(x)
forwarder(x) // Some(4) == foo(x)
forwarder(y) // Some(Some(4)) != foo(y) !!!
// Creating a forwarder changes semantics
To me this is already a deal breaker, currently I don’t need to reason about what is going on:
Either it works, or it doesn’t compile.
With this change, there might be tricky edge cases when I need to reason about “Will this parameter get wrapped ?”
Especially it looks like seemingly safe refactoring would actually change the semantics
I also 100% agree with this
And furthermore, I feel like no matter which of the two conversions we take, people will complain that we didn’t take the other one
(Either the “I want strong guarantees camp” or the “I just want it to work for obvious cases” camp)
Finally the one given use case,
can I think be better accomplished by adding some syntactic sugar:
def foo[T](x: T?): Option[T] = x // x has type Option[T] just like `T*` are `Seq[T]`
// Make the call-site both clear and predictible
foo() // None
foo(1) // Some(1)
foo(Some(1)) // Some(Some(1))
// Still allows full control when needed
foo(?None) // None
foo(?Some(1)) // Some(1)
foo(?Option.when(myCondition)(myValue)) // depends on myCondition
foo(?xs.headOption)
(I am currently writing a Pre-SIP for this, but it will still take some time, as I’m looking into all the edge-cases)