statically typed companion reference

234 views
Skip to first unread message

Paul Phillips

unread,
Feb 1, 2013, 1:04:59 PM2/1/13
to scala-l...@googlegroups.com
This took me a lot longer to get working than I care to admit. Maybe someone has already done this. Can we put something along these lines in the stdlib please.


For the TMC;DR crowd,

case class Bippy(x: Int)
val y = Bippy(5)
println(y.companion.apply(10))  // Bippy(10)

(Companion not being a method on Bippy or anything, it's a macro.)

Whereas this does not compile:

class Bar ; val y = new Bar
println(y.companion) // 
error: Instance of s.Bar has no companion object
 
I am sure this could be taken much further but I am out of time. The floor is open!

Simon Ochsenreither

unread,
Feb 1, 2013, 1:07:18 PM2/1/13
to scala-l...@googlegroups.com
Wow, this is great!

What would be the right place for it? Predef?

Paul Phillips

unread,
Feb 1, 2013, 1:16:11 PM2/1/13
to scala-l...@googlegroups.com

On Fri, Feb 1, 2013 at 10:07 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
What would be the right place for it? Predef?

Everything I write belongs in Predef! I AM NAPOLEON!

Som Snytt

unread,
Feb 1, 2013, 1:25:14 PM2/1/13
to scala-l...@googlegroups.com
Scalac should just import Paulpdef before Predef.



--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Simon Ochsenreither

unread,
Feb 1, 2013, 1:57:08 PM2/1/13
to scala-l...@googlegroups.com
Let's exchange StringAdd with companion. Deal? :-D

Ken Scambler

unread,
Feb 1, 2013, 5:47:52 PM2/1/13
to scala-l...@googlegroups.com
How would this interact with libraries that have `companion` vals/defs typed a particular way, like GenericTraversableTemplate and its children?

--

Pavel Zalunin

unread,
Feb 7, 2013, 6:09:39 PM2/7/13
to scala-l...@googlegroups.com
Why not:

typeOps.companionOf[Beeps].apply(10) ?

2013/2/2 Ken Scambler <ken.sc...@gmail.com>

Miles Sabin

unread,
Feb 7, 2013, 6:21:37 PM2/7/13
to scala-l...@googlegroups.com


On 7 Feb 2013 18:09, "Pavel Zalunin" <wr4...@gmail.com> wrote:
>
> Why not:
>
> typeOps.companionOf[Beeps].apply(10) ?

Under what circumstances would you expect that to compile and yet,

  Beeps(10)

wouldn't?

Cheers,

Miles



Alois Cochard

unread,
Feb 8, 2013, 5:37:00 AM2/8/13
to scala-l...@googlegroups.com
This sound promising, but I don't understand how this work as the macro return an "AnyRef", how is the type inferred?

Eugene Burmako

unread,
Feb 8, 2013, 5:41:46 AM2/8/13
to scala-l...@googlegroups.com
The return type of a macro def is only a hint for the typechecker. Expansions will be typechecked against it, but they will end up having whatever most specific type they typecheck to: http://stackoverflow.com/questions/13669974/static-return-type-of-scala-macros/13673950#13673950.


--

Rodrigo Cano

unread,
Feb 8, 2013, 10:48:10 AM2/8/13
to scala-l...@googlegroups.com
>
> Why not:
>
> typeOps.companionOf[Beeps].apply(10) ?
Under what circumstances would you expect that to compile and yet,
  Beeps(10)
wouldn't?

 
I also don't see the benefit. Beneficial would be a typeclass Companion[T] provided with a macro, but a companion method over a known type?

Paul Phillips

unread,
Feb 8, 2013, 1:10:59 PM2/8/13
to scala-l...@googlegroups.com

On Fri, Feb 8, 2013 at 7:48 AM, Rodrigo Cano <ioni...@gmail.com> wrote:
I also don't see the benefit. Beneficial would be a typeclass Companion[T] provided with a macro, but a companion method over a known type?

I did kind of imply there was more to do. If the pieces are in place for implicitly[Companion[T]] to work correctly, then I can do that when I have time.

But that too is something of a failure; what I really want is

  def f[T <: HasCompanion] = implicitly[companionOf[T]]

where HasCompanion is a fictional type which translates into a compile time verification that

  typeOf[T].typeSymbol.companionSymbol.exists

Because if we take a step back, passing around an implicit parameter at runtime as evidence of something which was completely known at compile time is a little ridiculous. Is this one possible?

Jan Vanek

unread,
Feb 8, 2013, 5:14:15 PM2/8/13
to scala-l...@googlegroups.com
Hi Paul,

I didn't write a macro yet, so what comes maybe wrong or blatantly obvious.

Suppose we have a macro def companion[T] = macro which returns the companion of T, such that when there is class A; object A; then companion[A] = A (where = means "same instance, same type"). Then such macro has sense only when used within another macro def or macro type with a type parameter, for which we want a companion. Such macro can't work when used within a plain def foo[T] = something method, because of erasure.

What are you after?

Jan

Paul Phillips

unread,
Feb 12, 2013, 12:12:08 PM2/12/13
to scala-l...@googlegroups.com

On Fri, Feb 8, 2013 at 2:14 PM, Jan Vanek <j3v...@gmail.com> wrote:
I didn't write a macro yet, so what comes maybe wrong or blatantly obvious.

Suppose we have a macro def companion[T] = macro which returns the companion of T, such that when there is class A; object A; then companion[A] = A (where = means "same instance, same type"). Then such macro has sense only when used within another macro def or macro type with a type parameter, for which we want a companion. Such macro can't work when used within a plain def foo[T] = something method, because of erasure.

No, it can't work with def foo[T] because we don't know T is something with a companion, and that is the testimony we require from the compiler. But it can work with

  def f[U, T <: HasCompanion[U]](x: T)

or less optimally but perhaps more easily given current macro capabilities

  def[T, U](x: T)(implicit ev: Companions[T, U]]

Rich Oliver

unread,
Feb 28, 2013, 12:37:00 PM2/28/13
to scala-l...@googlegroups.com
On Friday, February 8, 2013 6:10:59 PM UTC, Paul Phillips wrote:
Because if we take a step back, passing around an implicit parameter at runtime as evidence of something which was completely known at compile time is a little ridiculous. Is this one possible?
When I finally groked Type Class's a couple of weeks back I was horrified. Doesn't this just scream out for static methods. Isn't this trivial in C# and Java? Why can't we have static methods and implicits? Why does it have to be either or? Yes sometimes you want the flexibility of call site polymorphism on a type class but often you don't.

Static methods could still be paced in an auto created companion object as they are with case classes. Static methods don't have to mean any weakening of object orientation. We could then have static type bounds including generic static type bounds. Context bounds are limited in many circumstances. They often have an unwanted value parameter, which could be expensive for fast a method or small object creation. They can't take type parameters and you can't use them as a type parameter for example if you want to create a list of Numerics: List[Numeric] or a list of ordering: List[Ordering].

Scala is too complicated. Its too complicated to create a static method, or a proper enum. That should be easily fixable, but unfortunately Scala seems to have developed a bad case of keyword asceticism. I think Scala might be in danger of making the same mistake with Macros that C++ did with Templates and Macros. Templates and Macros were excellent features, but the language relied on them too much. They should be a last resort for the expert programmer, Common patterns should be facilitated by the main compiler.

Jan Vanek

unread,
Apr 8, 2013, 5:46:53 PM4/8/13
to scala-l...@googlegroups.com
Hi Paul,

https://gist.github.com/j3vanek/5340747

the gist is an example of placing constraints on the companion objects inside the companion classes/traits. I am not sure about its utility, so it laid 2 months. I am posting after seeing your mail in "pattern matcher correctness and depending on implicit values" thread, in the link:

https://issues.scala-lang.org/browse/SI-7348

It appears that in:

  type ThisType >: Null <: AnyRef with SingletonType with ThisTypeApi
  val ThisType: ThisTypeExtractor
  abstract class ThisTypeExtractor {
    // we could write an implicit Foo => ThisType here
    // and it would never be found
    def apply(sym: Symbol): Type
    def unapply(tpe: ThisType): Option[Symbol]
  }

one actually wants to place a constraint on the companion of the ThisType.

Regards,
Jan

Chris Hodapp

unread,
Apr 9, 2013, 3:50:24 PM4/9/13
to scala-l...@googlegroups.com
But you can take a List[Numeric]: def foo[T: Numeric](x: List[T]) = x.sum

But you can have them take type parameters (this one is a bit longer and can be entered on the REPL in paste mode):

import language.higherKinds

trait Unwrapable[Wrapper[_]] { def unwrap[S](x: Wrapper[S]): S  }
implicit object OptionIsUnwrapable extends Unwrapable[Option] { def unwrap[S](x: Option[S]): S = x.get }

def test[T[_]: Unwrapable](x: T[Int]) = implicitly[Unwrapable[T]].unwrap(x)
test[Option](Some(5))

Tom Switzer

unread,
Apr 9, 2013, 4:00:22 PM4/9/13
to scala-l...@googlegroups.com
I'm not entirely sure you accurately groked type classes, since they are certainly not usable in Java or C#. In fact, type classes were the reason I originally hopped over to Scala from Java. An example of a "type class" in Java would be Comparable<T>, as it is used in TreeMap, for instance. I am unsure how static methods would help.

If you want an example, think about how you would write a method in Java that works with any numeric type. You want to write it once, but abstract over the number type used (eg. Int, Double, BigDecimal, etc.). Keep in mind, you can't use inheritance, since you control none of those classes.


--

John Nilsson

unread,
Apr 9, 2013, 4:33:46 PM4/9/13
to scala-l...@googlegroups.com

I've written a post on Type Classes from a C# perspective here http://curiousskeptic.com/type-classes-for-c-programmers/

One thing I concluded was that Type Classes probably could be implemented relatively easy as a T4 template, combining extension methods and code generation. The result could even be distributed as a NuGet package.

BR
John

Tom Switzer

unread,
Apr 9, 2013, 5:34:40 PM4/9/13
to scala-l...@googlegroups.com
John,

I created a very similar approach in Java, using dependency injection to "implicitly" wire up my type class instances. This was before I knew what type classes were, so you could imagine how I felt when I found out that Scala basically did everything I wanted, but without all the crazy DI ugliness and brittleness. Moreover, since the JVM doesn't have reified generics, in order to do any DI, you had to explicitly subtype your class that required the monoid, getting rid of the type parameter, so that the erasure would stick around.

Your T4/code gen approach loses out on what makes type classes great though - extensibility. It's also purely .Net, since the overloading relies on reified generics. Nifty though.

Anyways, in all cases, I'm still going to say that Scala's solution to type classes is significantly better than Java or C#'s.

Cheers,
Tom

Tom Switzer

unread,
Apr 9, 2013, 5:36:21 PM4/9/13
to scala-l...@googlegroups.com
Scatch my comment regarding extensibility, just noticed they're extension methods. Sorry! :)
Reply all
Reply to author
Forward
0 new messages