|
Howdy,
In 2.8, is it possible to have the manifest for a type parameter defined on a trait reified someplace that's accessible to the trait. The following works with classes: class Foo[T: Manifest] { def myType(implicit man: Manifest[T]): Manifest[T] = man } But it seems that because traits don't take parameters, you can't do trait Foo[T: Manifest] .... It would be very, very helpful to have a reification of trait type parameters. Any ideas on how this could be done? Thanks, David -- Lift, the simply functional web framework http://liftweb.net Beginning Scala http://www.apress.com/book/view/1430219890 Follow me: http://twitter.com/dpp Surf the harmonics |
|
I was hopeful you could do something clever with Predef.=:=, however
the type system doesn't know that Manifest[TT] is the same as Manifest[T] and the type inferencer can't infer TT, which renders this approach useless. trait Foo[T] { def myType[TT](implicit man: Manifest[TT], tteqt: TT =:= T): Manifest[TT] = man } //(new Foo[Int] {}).myType //compile error (new Foo[Int] {}).myType[Int] //(new Foo[Int] {}).myType[String] //compile error Alternatively, you could provide the manifest as a member: trait Foo[T] { def myType: Manifest[T] } class Bar[X: Manifest] extends Foo[X] { val myType: Manifest[X] = implicitly } new Bar[Int].myType Hopefully someone else has a better way! -jason On Fri, Feb 5, 2010 at 2:25 AM, David Pollak <[hidden email]> wrote: > Howdy, > > In 2.8, is it possible to have the manifest for a type parameter defined on > a trait reified someplace that's accessible to the trait. The following > works with classes: > > class Foo[T: Manifest] { > def myType(implicit man: Manifest[T]): Manifest[T] = man > } > > But it seems that because traits don't take parameters, you can't do trait > Foo[T: Manifest] .... > > It would be very, very helpful to have a reification of trait type > parameters. Any ideas on how this could be done? > > Thanks, > > David > > -- > Lift, the simply functional web framework http://liftweb.net > Beginning Scala http://www.apress.com/book/view/1430219890 > Follow me: http://twitter.com/dpp > Surf the harmonics > |
trait Foo[T] { You can get pretty close just using type bounds: trait Foo[T] { def myType[TT >: T <: T : Manifest]: Manifest[TT] = implicitly } - Colin |
|
I'm pretty sure you can just do: trait Foo[T] { You would have to include the manifest parameter in every function that needs the manifest and it would have to be available in every scope that called one of those functions but it should work. I have used it. -Arthur (sent from phone) On Feb 5, 2010 8:26 AM, "Colin Bullock" <[hidden email]> wrote: |
|
On Fri, Feb 5, 2010 at 8:58 AM, Arthur Peters <[hidden email]> wrote:
Ah... of course... ***goes to get more coffee*** - Colin |
|
In reply to this post by Arthur Peters
On Fri, Feb 5, 2010 at 6:58 AM, Arthur Peters <[hidden email]> wrote:
You actually can't do this because T is not know in every context. For example: object Bar { def calcFooManifest[T](foo: Foo[T]): Manifest[T] = foo.myType // we don't know what T is here } So... the net-net is that traits don't take constructor parameters for a bunch of well know and documented reasons. Traits do take type parameters. It seems to me that in the case of traits with type parameters marked as Manifest could only be mixed into classes that have the corresponding type parameter marked as Manifest... just like abstract methods on a trait must be implemented on implementing classes. In the case of an anonymous class that's being instantiated (new Trait[DefinedType] {}), the type is known and the anonymous class could contain the Manifest. I guess the third case is "class Foo extends Trait[Int]" but in that case, the compiler knows the type and can generate a manifest for it. This would be *very helpful*.
-- Lift, the simply functional web framework http://liftweb.net Beginning Scala http://www.apress.com/book/view/1430219890 Follow me: http://twitter.com/dpp Surf the harmonics |
|
On Fri, Feb 5, 2010 at 6:17 PM, David Pollak
<[hidden email]> wrote: > On Fri, Feb 5, 2010 at 6:58 AM, Arthur Peters <[hidden email]> wrote: >> >> I'm pretty sure you can just do: >> >> trait Foo[T] { >> def func(...)(implicit mt : Manifest[T]) = ... >> } > > You actually can't do this because T is not know in every context. > > > For example: > > object Bar { > def calcFooManifest[T](foo: Foo[T]): Manifest[T] = foo.myType // we don't > know what T is here > } > > So... the net-net is that traits don't take constructor parameters for a > bunch of well know and documented reasons. Traits do take type parameters. > It seems to me that in the case of traits with type parameters marked as > Manifest could only be mixed into classes that have the corresponding type > parameter marked as Manifest... just like abstract methods on a trait must > be implemented on implementing classes. So you mean trait Test[X: Manifest[X]] would be desugared as trait Test[X] { implicit val mfX: Manifest[X] } and class Impl[X: Manifest[X]] extends Test[X] would desugar as class Impl[X](implicit val mfX: Manifest[X]) extends Test[X] ? That seems reasonable to me. > In the case of an anonymous class > that's being instantiated (new Trait[DefinedType] {}), the type is known and > the anonymous class could contain the Manifest. I guess the third case is > "class Foo extends Trait[Int]" but in that case, the compiler knows the type > and can generate a manifest for it. So, it boils down to this: Exactly as the compiler can find implicit *parameter* values for method calls, it should find implicits for a class' abstract member values. Is that possible? -- Johannes ----------------------------------------------- Johannes Rudolph http://virtual-void.net |
|
On Fri, Feb 5, 2010 at 6:29 PM, Johannes Rudolph
<[hidden email]> wrote: > So you mean > > trait Test[X: Manifest[X]] That's of course trait Test[X: Manifest] a.s.o. -- Johannes ----------------------------------------------- Johannes Rudolph http://virtual-void.net |
|
In reply to this post by dpp
It can be done manually, it just requires a bit of housekeeping:
trait A[T] { implicit val t: Manifest[T] } class B extends A[Int] { override val t = manifest[Int] } The possibility of having traits take constructor parameters has been mentioned before. I don't know how far work on this front has gone, but I suspect that more general feature would solve this particular problem. --j On Thu, Feb 4, 2010 at 5:25 PM, David Pollak <[hidden email]> wrote: Howdy, |
|
I can't corroborate this personally, but I've been told (by someone in the field of type theory) that traits would be generally unsound if they could take constructor parameters. I don't know if this holds for implicit params (I would imagine so). As I said, I can't personally confirm this information, but I thought it was worth throwing out there. Daniel
|
|
I know nothing of type theory, but I do know that adding trait constructor parameters has been mentioned by the Scala team in the past. See, for example, this thread: http://old.nabble.com/-scala--Constructor-parameters-for-traits-to20796791.html
In particular, Ingo Maier's response. --j On Sat, Feb 6, 2010 at 3:43 PM, Daniel Spiewak <[hidden email]> wrote:
|
|
And also this one with a response from Donna Malayeri:
On Sun, Feb 7, 2010 at 5:35 AM, Jorge Ortiz <[hidden email]> wrote: I know nothing of type theory, but I do know that adding trait constructor parameters has been mentioned by the Scala team in the past. See, for example, this thread: http://old.nabble.com/-scala--Constructor-parameters-for-traits-to20796791.html |
|
In reply to this post by Daniel Spiewak
No, there wouldn't be unsoundness per-se, but there would be problems in initializing objects, due to the diamond problem.
I'm looking into solutions; my current thoughts are to disallow diamond inheritance when a trait has constructor parameters. If you do need diamond inheritance AND constructor parameters, you should restructure your class hierarchy using self-types (see the post someone mentioned, http://old.nabble.com/Re:-How-are-Trait-Constructors-coming-along--p26994243.html). The manifest example is a great use case, so I'll see if I can devote more time to the trait constructor parameter issue. Donna On Sun, Feb 7, 2010 at 12:43 AM, Daniel Spiewak <[hidden email]> wrote:
|
|
In reply to this post by Daniel Spiewak
If traits had constructor parameters then Scala would have full blown multiple inheritance the same way C++ does. That's not unsound, but it's very tricky to deal with diamond inheritance.
trait Foo(val x : Int) trait Bar extends Foo(1) trait Baz extends Foo(2) object Quuz extends Bar with Baz What's the value of Quux.x? On Sat, Feb 6, 2010 at 3:43 PM, Daniel Spiewak <[hidden email]> wrote:
|
|
> trait Foo(val x : Int) > trait Bar extends Foo(1) > trait Baz extends Foo(2) > object Quuz extends Bar with Baz > What's the value of Quux.x? It's not really that different from the current view on methods, I suppose. Last added implementation wins (Quux.x == 2), so there shouldn't be any ambiguities. --- Jan. |
|
In reply to this post by James Iry-2
Scala uses a kind of linearization here, so I think you could get away with it, as every class/object/trait had a defined path to the parent. It may be awkward to wield though, like virtual inheritance. Sent from my iPhone
|
|
In reply to this post by James Iry-2
Hello James,
As pointed out by others, the current rules of linearisation can be applied to your example, or am I missing something? How about the following example: trait Foo(val x : Int) trait Bar(val x: Int) extends Foo(x) trait Baz(val x: Int) extends Foo(x) object Quuz extends Bar(1) with Baz(2) The linearisation of the last line is Quizz -> Baz -> Bar -> Foo. But this can not work given that Baz can not be initialized without also initializing Foo (because of the constructors).
Is that the real issue with trait constructors? Thanks, Mushtaq On Fri, Feb 19, 2010 at 9:46 PM, James Iry <[hidden email]> wrote: If traits had constructor parameters then Scala would have full blown multiple inheritance the same way C++ does. That's not unsound, but it's very tricky to deal with diamond inheritance. |
|
I believe that is the issue James was getting at. It can be "solved" be linearizing super initializations and picking a "winner" for arguments, but gets rather unwieldy very quickly. Sent from my iPhone
|
|
just to illustrate the issues with trait constructor arguments with a quick, contrived, example: (example with implicit Manifest arguments left as an exercise to the reader) trait OutputStream extends Stream(512)
trait InputStream extends Stream(256) class IOStream extends InputStream with OutputStream // pick 512? // or: class IOStream extends OutputStream with InputStream // pick 256?
in principle, we don't want linearisation order to influence program behaviour (*), as it's supposed to stay behind the scenes In situations like this, I guess the compiler could require the programmer to disambiguate by explicitly specifying the constructor arguments that are multiply inherited:
class IOStream extends Stream(override 512) with InputStream with OutputStream not sure whether we should require the override keyword, but I like the symmetry with the example below (*)
cheers adriaan (*) that's why this is not allowed: trait A { def foo = 1 } trait B { def foo = 2
} trait AB extends A with B /* but this fixes it: { override def foo = 3 } */ |
|
On 2/22/10 10:15 AM, Adriaan Moors wrote:
> in principle, we don't want linearisation order to influence program > behaviour (*), as it's supposed to stay behind the scenes I am not sure. It already does influence program behavior: trait A { def f = 0 } trait B extends A { override def f = 2 * super.f } trait C extends A { override def f = 1 + super.f } (new B with C).f == 1 (new C with B).f == 2 While I agree that an override modifier of some sort might be nice, in our current proposal trait constructors will have different semantics from class contructors. Trait constructor arguments would rather designate a preference rather than a requirement for the value of a parameter which might be changed in the final class composition. I find the stream example would rather nicely fit into this line of thought, as client code dealing with traits probably shouldn't depend on the actual buffer size. In the end it would be a compromise between multiple inheritance with traits and single inheritance with classes but a guarantee for constructor arguments. I don't know in what sense Donna's proposal is different and whether it allows a more fine-grained compromise. Ingo > > In situations like this, I guess the compiler could require the > programmer to disambiguate by explicitly specifying the constructor > arguments that are multiply inherited: > > class IOStream extends Stream(override 512) with InputStream with > OutputStream > > not sure whether we should require the override keyword, but I like the > symmetry with the example below (*) > > cheers > adriaan > > (*) that's why this is not allowed: > trait A { > def foo = 1 > } > > trait B { > def foo = 2 > } > > trait AB extends A with B /* but this fixes it: { > override def foo = 3 > } */ |
| Powered by Nabble | Edit this page |
