Consider this trivial example:
class Outer {
case class Inner()
def test(i: Inner) = {}
}
As expected, this doesn't compile because of a type mismatch:
val o1 = new Outer()
val o2 = new Outer()
o1.test(o2.Inner()) // doesn't compile
What if we want to define a standalone function?
This is no good
def test[X <: Outer](a: X#Inner, b: X#Inner) = {}
because this compiles as if everything is OK
test(o1.Inner(), o2.Inner())
This works
def test(x: Outer)(a: x.Inner, b: x.Inner) = {}
because this compiles:
test(o1)(o1.Inner(), o1.Inner())
and this doesn't:
test(o1)(o1.Inner(), o2.Inner())
However we have to pass an extra Outer
argument to test
. Is it possible to avoid this? Ideally, the following should work:
test(o1.Inner(), o1.Inner()) // ok
test(o1.Inner(), o2.Inner()) // compilation error
There are many types of path-dependent options including Asian, chooser, lookback, and barrier options. A soft path dependent option bases its value on a single price event that occurred during the life of the option.
Path dependence is when the decisions presented to people are dependent on previous decisions or experiences made in the past.
A soft path dependent option bases its value on a single price event that occurred during the life of the option. A hard path dependent option takes into account the entire trading history of the underlying asset.
Path dependence has primarily been used in comparative-historical analyses of the development and persistence of institutions, whether they be social, political, or cultural. There are arguably two types of path-dependent processes:
I don't think that, out of the box, it's possible to enforce it in a satisfying way. For instance, one possible solution might be:
scala> def test[X <: Outer#Inner](a: X)(b: X) = ()
test: [X <: Outer#Inner](a: X)(b: X)Unit
scala> test(o1.Inner())(o1.Inner())
scala> test(o1.Inner())(o2.Inner())
<console>:16: error: type mismatch;
found : o2.Inner
required: o1.Inner
test(o1.Inner())(o2.Inner())
^
Looks good, but you can circumvent it by explicitly passing in the type arguments. (same goes for @OlivierBlanvillain's solution by the way)
scala> test[Outer#Inner](o1.Inner())(o2.Inner())
Now let's try the following:
scala> def test[X <: Outer](a: X#Inner)(b: X#Inner) = ()
test: [X <: Outer](a: X#Inner)(b: X#Inner)Unit
scala> test(o1.Inner())(o2.Inner())
Doesn't work, scalac infers X
to be Outer
, which isn't specific enough, and we could supply Outer
as explicit type argument anyway. We need a way to force X
to be a singleton type so that it can only represent the path o1
or o2
, and not some general type that can be inhabited by an infinite amount of values. There is a way. Scala has the marker trait Singleton
for this purpose. Let's try it:
scala> def test[X <: Outer with Singleton](a: X#Inner)(b: X#Inner) = ()
test: [X <: Outer with Singleton](a: X#Inner)(b: X#Inner)Unit
scala> test(o1.Inner())(o1.Inner())
<console>:15: error: inferred type arguments [Outer] do not conform to method test's type parameter bounds [X <: Outer with Singleton]
test(o1.Inner())(o1.Inner())
^
<console>:15: error: type mismatch;
found : o1.Inner
required: X#Inner
test(o1.Inner())(o1.Inner())
^
Now our valid case doesn't work anymore! The problem is that scalac refuses to infer singleton types. We have to pass them in explicitly:
scala> test[o1.type](o1.Inner())(o1.Inner())
The invalid cases don't work anymore:
scala> test(o1.Inner())(o2.Inner())
<console>:16: error: inferred type arguments [Outer] do not conform to method test's type parameter bounds [X <: Outer with Singleton]
test(o1.Inner())(o2.Inner())
^
<console>:16: error: type mismatch;
found : o1.Inner
required: X#Inner
test(o1.Inner())(o2.Inner())
^
scala> test[o1.type](o1.Inner())(o2.Inner())
<console>:16: error: type mismatch;
found : o2.Inner
required: o1.Inner
test[o1.type](o1.Inner())(o2.Inner())
^
scala> test[Outer](o1.Inner())(o2.Inner())
<console>:17: error: type arguments [Outer] do not conform to method test's type parameter bounds [X <: Outer with Singleton]
test[Outer](o1.Inner())(o2.Inner())
^
So this enforces the rules we want, but you have to pass in the types explicitly...
EDIT
Actually it turns out you can enforce this without losing type inference and without help from any external libraries, but you're probably not going to like it :-p
META EDIT as pointed out in the comments this can still be circumvented if you try hard enough, so I guess you're stuck with the above solution.
scala> import scala.language.existentials
import scala.language.existentials
scala> def test[X <: x.Inner forSome { val x: Outer }](a: X, b: X) = ()
test: [X <: x.Inner forSome { val x: Outer }](a: X, b: X)Unit
scala> test(o1.Inner(), o1.Inner())
scala> test(o1.Inner(), o2.Inner())
<console>:16: error: inferred type arguments [Outer#Inner] do not conform to method test's type parameter bounds [X <: x.Inner forSome { val x: Outer }]
test(o1.Inner(), o2.Inner())
^
<console>:16: error: type mismatch;
found : o1.Inner
required: X
test(o1.Inner(), o2.Inner())
^
<console>:16: error: type mismatch;
found : o2.Inner
required: X
test(o1.Inner(), o2.Inner())
^
scala> test[o1.Inner](o1.Inner(), o2.Inner())
<console>:16: error: type mismatch;
found : o2.Inner
required: o1.Inner
test[o1.Inner](o1.Inner(), o2.Inner())
^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With