scala - Strange type mismatch when using member access instead of extractor -
given tuple elements of type a
, type parametrised in a
:
trait writer[-a] { def write(a: a): unit } case class write[a](value: a, writer: writer[a])
and use site:
trait cache { def store[a](value: a, writer: writer[a]): unit }
why following work expected, using tuple's extractor:
def test1(set: set[write[_]], cache: cache): unit = set.foreach { case write(value, writer) => cache.store(value, writer) }
but following fails:
def test2(set: set[write[_]], cache: cache ): unit = set.foreach { write => cache.store(write.value, write.writer) }
with error message
found : writer[_$1] type _$1 required: writer[any] cache.store(write.value, write.writer) ^
can fix second form (test2
) compile properly?
edit
departing ideas owen tried out if can make work without pattern matching @ (which wanted in first place). here 2 more strange cases, 1 working, other not:
// not work def test3(set: set[write[_]], cache: cache): unit = { def process[a](write: write[a]): unit = cache.store(write.value, write.writer) set.foreach(process) } // _does work_ def test4(set: set[write[_]], cache: cache): unit = { def process[a](write: write[a]): unit = cache.store(write.value, write.writer) set.foreach(w => process(w)) }
still pretty obscure me...
running -xprint:typer
illuminating here. problem test2
there existential type, appears in 2 separate places: both write.value
, write.writer
have existential type, but, crucially, compiler has no way of knowing have same existentially quantified type variable. though access them same object, compiler forgets came same place.
when test1
typed, see:
def test1(set: set[write[_]], cache: cache) = set.foreach(((x0$1: write[_]) => x0$1 match { case (value: _$1, writer: writer[_$1])write[_$1]((value @ _), (writer @ _)) => cache.store[_$1](value, writer) }));
the type variable _$1
matched along values. matching type variable _$1
binds in scope of case
, it's not existential anymore, , scala can tell value
, writer
have same type parameter.
the solution test2
not use existentials:
def test2[a]( set: set[ write[ ]], cache: cache ) { set.foreach { write => cache.store( write.value, write.writer ) } }
or bind type variable match:
def test2( set: set[ write[ _ ]], cache: cache ) { set.foreach { case write: write[a] => cache.store( write.value, write.writer ) } }
edit
let me endeavor answer new questions brought up.
the reason test3
not work, when write:
set.foreach( process )
process
, polymorphic, has made monomorphic, 2 reasons (that know of):
functions in scala cannot polymorphic; methods can be.
process
defined method; when used first-class function, function.the way compiler type inference taking polymorphic values , unifying them make them less polymorphic. passing actual polymorphic value method argument require higher-rank types.
the reason test4
does work function literal:
set.foreach( w => process( w ))
is not polymorphic function! takes argument exestentially qualified type; not polymorphic type. calls method (not function) process
, , matches existential type variable process
's type parameter. pretty wild, eh?
you write:
set.foreach( process(_) )
which, creating anonymous function, means same thing.
another route may or may not find appropriate discard existential types , use type members:
trait writable { type val value: val writer: writer[a] } case class write[t]( value: t, writer: writer[ t ]) extends writable { type = t } def test2( set: set[writable], cache: cache ) { set.foreach { write => cache.store( write.value, write.writer ) } }
here scala able see write.value
, write.writer
have same type parameter because have same path dependent type.
Comments
Post a Comment