-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
eliminate instance boiler plate: seamless migration to type classes #62
Comments
I would actually go further and add the Of course the major problem at the moment is that it's just not nice navigating the type tree, and it's really unfriendly to the IDE. Also, see #50 and the fact that not everyone agrees on the right way to encode a typeclass. I would argue that the steps for this would vaguely be,
I'd also like to have an in depth look at |
Sounds good. Just wanted to record this proposal in a good place and hopefully some day we'll get to it. Would give it a shot myself, if I wasn't tied up with cbt already :). (cc @radsaggi, @clhodapp, @pedrofurla you also guys may wanna take a look at this. Maybe something fun for later in the year). |
Absolutely - I'm currently tied up in a few pull requests/enhancements for some other projects, but the reason I've stalled on #6 is that I want to submit a WIP of something similar, preferably before cats can go 1.0.0 snapshot. |
@xeno-by is meta ready for this sorts of stuff? |
How do you guys use the Symbol API? Do you do c.typecheck? |
@xeno-by There are type annotations and a few Is there any way to get type hierarchies using |
@yilinwei Not at the moment. scala.meta 1.x only supports syntactic APIs, so no name resolution, typechecking, evaluation, etc. I've taken a brief look at the codebase and can see that apart from syntactic APIs, you're using: 1) c.eval to evaluate arguments of annotations, 2) c.typecheck to check whether annotations like noop are really simulacrum's noop. For potential migration to meta-based annotations (this can happen when I fix https://github.com/scalameta/paradise/issues/1, https://github.com/scalameta/paradise/issues/2 and https://github.com/scalameta/paradise/issues/3), 1) can be worked around by manual inspection of arguments and looking for literals and 2) can be temporarily replaced by comparison of names, not symbols. |
@yilinwei I also see that you'd like to turn simulacrum into a compiler plugin. What kind of desired functionality is currently missing with macro annotations? |
@xeno-by the issue with whitebox macros is more that the presentation compiler (and by extension) IDE's handle them so poorly. Better IDE support would be really useful both for adoption and promoting the usability of some of these language extensions. |
@yilinwei I see. Improving IDE support is one of the key goal of scala.meta. We'll do our best. Btw what kind of problems do you run into with the presentation compiler? |
@xeno-by The presentation compiler happens before any whitebox macro expansion, which basically means any methods, classes generated by simulacrum aren't picked up by IDEs. As a compiler plugin we'd hopefully we able to promote most of it to the parser stage of the compilation, or at least enough of it such that the IDEs wouldn't have too much trouble. |
@yilinwei Well, actually it doesn't. Expansion of macro annotations happens in the phase called namer, and namer along with typer constitute Scala's presentation compiler. I think there's a different issue in play here. Could you provide more details about the troubles you're running into? |
@xeno-by Interesting, I had assumed that this wasn't the case based on the behavior I was seeing and on the discussion I had with @fommil, though I possibly misinterpreted. The problem is simply that - in all the IDE's that I've used with a macro type annotation, they don't recognize the classes/methods produced and cause the presentation compiler to underline any methods which I attempt to call as red. For example, see here. |
Yeah, I don't think this works @xeno-by 😞 Something I'd really like to get ready for my scalaworld talk with Rory is a good test harness that allows plugin / macro authors to write tests for how their libraries work in the presentation compiler. |
It would be interesting to sit together at Scala World and take a look at how exactly macros malfunction and maybe even come up with a plan how to address that. |
Definitely, I'm hoping to get some people in the typelevel compiler excitement interested. Step 1 is a good test harness, as per imaginary-friend's README |
I'm not going to have as much free time to work on github projects anymore, and with the impending crash I might not even be working with scala in 12 months 😭 |
I have mixed feelings on this. On the one hand, it's nice to remove some boilerplate. On the other hand, sometimes type class method names are necessarily abstract (like Also how would this be used to implement something like |
@ceedubs how does a type class instance implement the method of a type class trait using a more descriptive name? Can you give an example? Hehe, I guess you answered your own question about that second one there. If "it's more of a static method", how about we look for it in the companion? |
I said that data structures can often use more descriptive names. For example, consider the following trait Semigroup[A] {
def combine(a1: A, a2: A): A
} Here, Now say we write our own abstract class BigInt {
def +(other: BigInt): BigInt
}
object BigInt {
implicit val semigroupBigInt: Semigroup[BigInt] = new Semigroup[BigInt] {
def combine(a1: BigInt, a2: BigInt): BigInt = a1 + a2
}
} Should we have instead called the I guess people are always free to not use this annotation, so maybe my point doesn't matter. I guess it's just that one of the great features of type classes is polymorphism without coupling the data structure and the abstraction, so I'm wary of something that entices people to more strongly couple them for the sake of brevity. separate questionConsider the case where you were writing your own
|
@cvogt sorry, I kind of failed to say that I do appreciate the idea of trying to make type classes easier to work with in scala, and I'm glad that you brought up this idea. I just think that there are subtle complexities here that need to be considered. |
It's probably worth mentioning that I've recently done some work to reduce type class boilerplate in Scala. |
@ceedubs What about just letting you e.g. tag
The idea is that this would generate:
I know having too many annotations sucks... |
One major fact to keep in mind is that typeclass instances for type-parameterized classes are very often dependent on instances for their type arguments. For example... the most-reasonable |
At the moment in Scala one has to choose between trait-based interfaces with less boiler-plate or type-class based interfaces that scale to more use cases. Real world code bases either use only one concept and suffer the consequences. Or they use both concepts as needed but suffer from less regularity, more controversy and signiciant mechanical refactoring when changing your mind in a case. Simulacrum reduces the required boiler plate overhead for type classes but does not eliminate it entirely.
This is a proposal for a small addition to Simulacrum that would remove the remaining boiler plate and make migration between trait based interfaces and type-class based interfaces much more seamless. In a way it is very much in the spirit of Scala to integrate these OO / FP concepts and it has very concrete practical benefits. And it would basically put Simulacrum on an equal level with trait-based interfaces with regards to syntactic overhead.
So we know that type classes are more flexible than OO interface implementation. This is especially true with generics. A Scala class can't implement
Equals[Int]
andEquals[String]
at the same time. Implicit classes can help here, but they can't be inferred if the generic types depend on method arguments, not only on the left hand sides. Type-classes solve this problem. (Also see https://github.com/cvogt/traits-vs-type-classes/blob/master/traits-vs-type-classes.scala)But even with Simulacrum the required boiler plate can be significant. This is particularly annoying when the use case at hand does not require the additional flexibility. Here is an example:
requires more boiler plate than
This adds up when one has to do it everywhere. So here is my proposal to get the best of both worlds. We could extend Simulacrum with the following annotation macro, that allows "implementing" type classes directly from data types.
The usage would be as such:
This would automatically generate the following companion which simply forwards to the method on the class.
@typeclass
is the equivalent to OOclass
.@implements
is the equivalent toextends
.This would cut down on boiler plate and would make migration between the two concepts much more seamless. It's conceivable to additionaly generate an actual interface trait allowing even easier migration.
The text was updated successfully, but these errors were encountered: