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

Added a :kind command to the REPL #2340

Merged
merged 1 commit into from May 17, 2013

Conversation

Projects
None yet
8 participants
@folone
Contributor

folone commented Mar 31, 2013

Description

Because Scala supports working with higher kinded types, we might want to be able to inspects kinds as well as types. This pull request adds a simple :kind command, implemented by @eed3si9n in his "Introduction to Scalaz: day 4" post, to the Scala REPL. Here is an example output:

scala> :kind scala.Option
Option's kind is * -> *

scala> :k scalaz.Unapply
Unapply's kind is ((* -> *) -> *) -> * -> *

scala> import scalaz._
import scalaz._

scala> :k Monad // Finds locally imported types.
Monad's kind is (* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> :k Nonexisting
<console>:14: error: not found: value Nonexisting
              Nonexisting
              ^

scala> class Foo
defined class Foo

scala> new Foo { def empty = true }
res0: Foo{def empty: Boolean} = $anon$1@786aceba

scala> :k res0
Foo{def empty: Boolean}'s kind is *

Future improvements

As pointed out by @retronym here, variance and type bounds are also part of the kind and we might want to include them in command's output.

Acknowledgements

The function for :kind feature is originally implemented by @eed3si9n
I got a lot of help from @xeno-by and @retronym while working on this feature, thank you for that.

Happy Easter everyone!

@folone

This comment has been minimized.

Show comment
Hide comment
@folone

folone Mar 31, 2013

Contributor

I probably have to somehow assign this pull request to @paulp, but it looks like I don't have permissions to do that.

Contributor

folone commented Mar 31, 2013

I probably have to somehow assign this pull request to @paulp, but it looks like I don't have permissions to do that.

@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Mar 31, 2013

Member

Two things to address before we can consider this:

  1. Please add test cases. See test/files/run/constant-type.scala as an example. Use ./test/partest --update-check --show-log to create the .check file with expected output.
  2. I'd like to find a Scala syntax for expressing the kinds. What's wrong with: :k Option \n Option[A] ?

Oh, and happy Easter, too! 🐰

Member

retronym commented Mar 31, 2013

Two things to address before we can consider this:

  1. Please add test cases. See test/files/run/constant-type.scala as an example. Use ./test/partest --update-check --show-log to create the .check file with expected output.
  2. I'd like to find a Scala syntax for expressing the kinds. What's wrong with: :k Option \n Option[A] ?

Oh, and happy Easter, too! 🐰

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Mar 31, 2013

Member

@retronym

What's wrong with: :k Option \n Option[A] ?

Using Scala syntax does add the ability to show variance and bounds and we might even get it for free from simply displaying the signature from Type. On the other hand, this would return kinds for Array as Array[A], which is different kind from Option[A], but they are both * -> *.

Anonymizing them both as F[A] I think is better. Not sure if there are conventions on the letters beyond the first two, but we could do something like: A, F, X, Y, Z, O, P, Q, ...; and number them as F[A1, A2].

Member

eed3si9n commented Mar 31, 2013

@retronym

What's wrong with: :k Option \n Option[A] ?

Using Scala syntax does add the ability to show variance and bounds and we might even get it for free from simply displaying the signature from Type. On the other hand, this would return kinds for Array as Array[A], which is different kind from Option[A], but they are both * -> *.

Anonymizing them both as F[A] I think is better. Not sure if there are conventions on the letters beyond the first two, but we could do something like: A, F, X, Y, Z, O, P, Q, ...; and number them as F[A1, A2].

@folone

This comment has been minimized.

Show comment
Hide comment
@folone

folone Mar 31, 2013

Contributor

@retronym @eed3si9n I'm also not sure if things like Unapply[TC[_[_]], MA] are more readable/understandable than ((* -> *) -> *) -> * -> *

Contributor

folone commented Mar 31, 2013

@retronym @eed3si9n I'm also not sure if things like Unapply[TC[_[_]], MA] are more readable/understandable than ((* -> *) -> *) -> * -> *

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Mar 31, 2013

Member

@folone For the majority of Scala users (assuming without any Haskell background) F[A1, A2] and X[F[A]] are probably easier to understand than the curried notation of * -> * -> * and (* -> *) -> *.

Unapply[TC[_[_]], MA] in my proposed alphabet soup would be Y[X[F[A1]], A2], which is not bad once you mentally map X to be a functor typeclass.

Member

eed3si9n commented Mar 31, 2013

@folone For the majority of Scala users (assuming without any Haskell background) F[A1, A2] and X[F[A]] are probably easier to understand than the curried notation of * -> * -> * and (* -> *) -> *.

Unapply[TC[_[_]], MA] in my proposed alphabet soup would be Y[X[F[A1]], A2], which is not bad once you mentally map X to be a functor typeclass.

@som-snytt

This comment has been minimized.

Show comment
Hide comment
@som-snytt

som-snytt Mar 31, 2013

Contributor

For the test, I was going to suggest overriding toString; but I don't know if that was an extraneous part of the test.

Contributor

som-snytt commented Mar 31, 2013

For the test, I was going to suggest overriding toString; but I don't know if that was an extraneous part of the test.

@seanparsons

This comment has been minimized.

Show comment
Hide comment
@seanparsons

seanparsons Mar 31, 2013

I agree it would be preferable to have a form that is something that we can use in Scala code.

seanparsons commented Mar 31, 2013

I agree it would be preferable to have a form that is something that we can use in Scala code.

@folone

This comment has been minimized.

Show comment
Hide comment
@folone

folone Mar 31, 2013

Contributor

PLS REBUILD ALL

Contributor

folone commented Mar 31, 2013

PLS REBUILD ALL

@scala-jenkins

This comment has been minimized.

Show comment
Hide comment
@scala-jenkins

scala-jenkins Mar 31, 2013

(kitty-note-to-self: ignore 15697719)
🐱 Roger! Rebuilding pr-rangepos-per-commit, pr-checkin-per-commit for 7855b08f, c9314fb8, c4245ae6. 🚨

scala-jenkins commented Mar 31, 2013

(kitty-note-to-self: ignore 15697719)
🐱 Roger! Rebuilding pr-rangepos-per-commit, pr-checkin-per-commit for 7855b08f, c9314fb8, c4245ae6. 🚨

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 1, 2013

Member

Created topic/kind2 branch under folone/scala: folone/scala@ca9edc1
This implements Scala syntax and variance. Not sure if I can get the bounds staying generic at API level.

scala> :k Int
Int's kind is A

scala> :k -v Either
Either's kind is F[+A1, +A2]
This is a type constructor: a 1st-order-kinded type.

scala> :k -v scalaz.Monad
Monad's kind is X[F[A]]
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> :k scalaz.Unapply
Unapply's kind is Y[X[F[A1]], A2]
Member

eed3si9n commented Apr 1, 2013

Created topic/kind2 branch under folone/scala: folone/scala@ca9edc1
This implements Scala syntax and variance. Not sure if I can get the bounds staying generic at API level.

scala> :k Int
Int's kind is A

scala> :k -v Either
Either's kind is F[+A1, +A2]
This is a type constructor: a 1st-order-kinded type.

scala> :k -v scalaz.Monad
Monad's kind is X[F[A]]
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> :k scalaz.Unapply
Unapply's kind is Y[X[F[A1]], A2]
@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Apr 1, 2013

Member

I'm not really sure what makes the most sense. Just to add a bit more confusion:

scala> typeOf[Either[Any, Any]].typeConstructor.etaExpand
res10: $r.intp.global.Type = [+A, +B]Either[A,B]

scala> trait Monad[F[_]]
warning: there were 1 feature warning(s); re-run with -feature for details
defined trait Monad

scala> typeOf[Monad[Any]].typeConstructor.etaExpand
res11: $r.intp.global.Type = [F[_]]Monad[F]

scala> trait Unapply[TC[_[_]], MA]
warning: there were 2 feature warning(s); re-run with -feature for details
defined trait Unapply

scala> typeOf[Unapply[Any, Any]].typeConstructor.etaExpand
res12: $r.intp.global.Type = [TC[_[_]], MA]Unapply[TC,MA]
Member

retronym commented Apr 1, 2013

I'm not really sure what makes the most sense. Just to add a bit more confusion:

scala> typeOf[Either[Any, Any]].typeConstructor.etaExpand
res10: $r.intp.global.Type = [+A, +B]Either[A,B]

scala> trait Monad[F[_]]
warning: there were 1 feature warning(s); re-run with -feature for details
defined trait Monad

scala> typeOf[Monad[Any]].typeConstructor.etaExpand
res11: $r.intp.global.Type = [F[_]]Monad[F]

scala> trait Unapply[TC[_[_]], MA]
warning: there were 2 feature warning(s); re-run with -feature for details
defined trait Unapply

scala> typeOf[Unapply[Any, Any]].typeConstructor.etaExpand
res12: $r.intp.global.Type = [TC[_[_]], MA]Unapply[TC,MA]
@paulp

This comment has been minimized.

Show comment
Hide comment
@paulp

paulp Apr 1, 2013

Contributor

For any which go deeper than a single type constructor, how about something like this - I probably blew the logic since I'm jumping on a plane, but wave your hands and make it right, you get the gist.

scala> :k Unapply
Unapply[TC[_2], MA]
  where _1 =    [A1] => F[A1]   // (* -> *)
  where _2 = [_2[F]] => X[_2]   // (* -> *) -> *)

I like this less than what I pictured when I started it, but I think some sort of decomposition is unavoidable.

Contributor

paulp commented Apr 1, 2013

For any which go deeper than a single type constructor, how about something like this - I probably blew the logic since I'm jumping on a plane, but wave your hands and make it right, you get the gist.

scala> :k Unapply
Unapply[TC[_2], MA]
  where _1 =    [A1] => F[A1]   // (* -> *)
  where _2 = [_2[F]] => X[_2]   // (* -> *) -> *)

I like this less than what I pictured when I started it, but I think some sort of decomposition is unavoidable.

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 1, 2013

Member

Unapply show down.

standard notation

((* -> *) -> *) -> * -> *

Eugene's Scala notation

Y[X[F[A1]], A2]

REPL's current notation for eta-expanded type constructor

[TC[_[_]], MA]Unapply[TC,MA]

Paul's notation

I'm interpreting Paul's suggestion to be "Use function-like syntax to express type constructor":

[_1, MA] => Unapply[_1, MA]
  where _1 = [_2] => X[_2]
  where _2 = [A1] => F[A1]

why mine is better

If you put trait in front, it compiles. Or better way of looking at it is, if the given expression appeared in a larger type, the entire type expression parses correctly as type variables. In other words,

scala> trait Foo[Y[X[F[A1]], A2]]
warning: there were 3 feature warning(s); re-run with -feature for details
defined trait Foo

Using _ means that you're not interested in using the type variable later. I don't think TC[_[_]] makes it any better than X[F[A1]].

The naming issue. Let's step back and ask the type of 1:

scala> :t 1
Int

Yea, REPL gives Int. Now what's the kind of Int?

scala> :k Int
Int's kind is A

A or in standard notation * can be the only answer here, not Int, because that's like answering 1's type is 1. Seemingly-intuitive, but not right, because it doesn't match 2's type. Similarly, Int's kind should match up that of Double's; Option's with List's, etc.

Keeping the original type variable names coud be confusing too. Some people use M instead of F. Others are using F to mean *. For example, Scalaz 7 codegens F for the type argument of both Functor and Monoid (to avoid confusion with A used for method type params?).

scala> :k scalaz.Monoid
Monoid's kind is F[A]
Member

eed3si9n commented Apr 1, 2013

Unapply show down.

standard notation

((* -> *) -> *) -> * -> *

Eugene's Scala notation

Y[X[F[A1]], A2]

REPL's current notation for eta-expanded type constructor

[TC[_[_]], MA]Unapply[TC,MA]

Paul's notation

I'm interpreting Paul's suggestion to be "Use function-like syntax to express type constructor":

[_1, MA] => Unapply[_1, MA]
  where _1 = [_2] => X[_2]
  where _2 = [A1] => F[A1]

why mine is better

If you put trait in front, it compiles. Or better way of looking at it is, if the given expression appeared in a larger type, the entire type expression parses correctly as type variables. In other words,

scala> trait Foo[Y[X[F[A1]], A2]]
warning: there were 3 feature warning(s); re-run with -feature for details
defined trait Foo

Using _ means that you're not interested in using the type variable later. I don't think TC[_[_]] makes it any better than X[F[A1]].

The naming issue. Let's step back and ask the type of 1:

scala> :t 1
Int

Yea, REPL gives Int. Now what's the kind of Int?

scala> :k Int
Int's kind is A

A or in standard notation * can be the only answer here, not Int, because that's like answering 1's type is 1. Seemingly-intuitive, but not right, because it doesn't match 2's type. Similarly, Int's kind should match up that of Double's; Option's with List's, etc.

Keeping the original type variable names coud be confusing too. Some people use M instead of F. Others are using F to mean *. For example, Scalaz 7 codegens F for the type argument of both Functor and Monoid (to avoid confusion with A used for method type params?).

scala> :k scalaz.Monoid
Monoid's kind is F[A]
@folone

This comment has been minimized.

Show comment
Hide comment
@folone

folone Apr 2, 2013

Contributor

👍 I like @eed3si9n's notation. We could also consider adding other notation(s) to the verbose output. Something like:

scala> :k Unapply
Unapply's kind is Y[X[F[A1]], A2]

scala> :k -v Unapply
Unapply's kind is Y[X[F[A1]], A2]
In standard kind notation: ((* -> *) -> *) -> * -> *
In decomposed notation:
[_1, MA] => Unapply[_1, MA]
  where _1 = [_2] => X[_2]
  where _2 = [A1] => F[A1]
This is a type constructor that takes type constructor(s): a higher-kinded type.
Contributor

folone commented Apr 2, 2013

👍 I like @eed3si9n's notation. We could also consider adding other notation(s) to the verbose output. Something like:

scala> :k Unapply
Unapply's kind is Y[X[F[A1]], A2]

scala> :k -v Unapply
Unapply's kind is Y[X[F[A1]], A2]
In standard kind notation: ((* -> *) -> *) -> * -> *
In decomposed notation:
[_1, MA] => Unapply[_1, MA]
  where _1 = [_2] => X[_2]
  where _2 = [A1] => F[A1]
This is a type constructor that takes type constructor(s): a higher-kinded type.
@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Apr 2, 2013

Member

For completeness, I should point out that kinds also track bounds and variance, as discussed in http://adriaanm.github.com/files/higher.pdf

Member

adriaanm commented Apr 2, 2013

For completeness, I should point out that kinds also track bounds and variance, as discussed in http://adriaanm.github.com/files/higher.pdf

@paulp

This comment has been minimized.

Show comment
Hide comment
@paulp

paulp Apr 2, 2013

Contributor

Adriaan's comment is part of why I was devising my plane-aborted expanded notation. If we're seeking a "scala syntax" it must be capable of expressing type constructor variance and bounds, which means a bunch of stars won't suffice, and underscores will only take you so far.

Contributor

paulp commented Apr 2, 2013

Adriaan's comment is part of why I was devising my plane-aborted expanded notation. If we're seeking a "scala syntax" it must be capable of expressing type constructor variance and bounds, which means a bunch of stars won't suffice, and underscores will only take you so far.

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 2, 2013

Member

@paulp Isn't "Scala syntax" essentially the same as the type parameter expression we write between the braces?

trait Foo[F[A <: Ordered[A]] <: Iterable[A]]

trait Foo[F[+A]]
Member

eed3si9n commented Apr 2, 2013

@paulp Isn't "Scala syntax" essentially the same as the type parameter expression we write between the braces?

trait Foo[F[A <: Ordered[A]] <: Iterable[A]]

trait Foo[F[+A]]
@paulp

This comment has been minimized.

Show comment
Hide comment
@paulp

paulp Apr 2, 2013

Contributor

The problems arise at the boundaries. I assume we're all familiar with the problem in this one:

trait Foo[CC[_] <: Traversable[_]]

The collision between an existential and a "don't care" placeholder type argument reaches tragic proportions if we'd like to come anywhere near optimal syntax for expressing kinds.

Even this can mean two different things

Foo[F[_]]

You have to know whether Foo's type argument is * -> * or * in order to know what F is doing there. But if the point of it is to communicate Foo's kind... I think we tolerate an unacceptable level of syntactic ambiguity.

Contributor

paulp commented Apr 2, 2013

The problems arise at the boundaries. I assume we're all familiar with the problem in this one:

trait Foo[CC[_] <: Traversable[_]]

The collision between an existential and a "don't care" placeholder type argument reaches tragic proportions if we'd like to come anywhere near optimal syntax for expressing kinds.

Even this can mean two different things

Foo[F[_]]

You have to know whether Foo's type argument is * -> * or * in order to know what F is doing there. But if the point of it is to communicate Foo's kind... I think we tolerate an unacceptable level of syntactic ambiguity.

@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/Kind.scala
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/Kind.scala
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/Kind.scala
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/Kind.scala
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/Kind.scala
@paulp

View changes

Show outdated Hide outdated test/files/run/kind-repl-command.check
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/package.scala
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/package.scala
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/ILoop.scala
@paulp

View changes

Show outdated Hide outdated src/repl/scala/tools/nsc/interpreter/package.scala
@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 3, 2013

Member

Since we're discussing implementation, we have a loose consensus on attempting Scala syntax as kind notation? If so could we roll back the last commit "Addressed some of issues, raised by @paulp" and merge folone/scala: folone/scala@ca9edc1 from topic/kind2 branch first? That's where Scala syntax notation is and it does address some of the points raised by Paul.

@folone I'd also like to get a chance to review the review before incorporating it.

Member

eed3si9n commented Apr 3, 2013

Since we're discussing implementation, we have a loose consensus on attempting Scala syntax as kind notation? If so could we roll back the last commit "Addressed some of issues, raised by @paulp" and merge folone/scala: folone/scala@ca9edc1 from topic/kind2 branch first? That's where Scala syntax notation is and it does address some of the points raised by Paul.

@folone I'd also like to get a chance to review the review before incorporating it.

@folone

This comment has been minimized.

Show comment
Hide comment
@folone

folone Apr 3, 2013

Contributor

@eed3si9n sure, go ahead. I actually should have done this in another branch.

Contributor

folone commented Apr 3, 2013

@eed3si9n sure, go ahead. I actually should have done this in another branch.

@paulp

This comment has been minimized.

Show comment
Hide comment
@paulp

paulp Apr 5, 2013

Contributor

There are too many ways to do this sort of thing, but long ago (before reflection) I put this in global, which still seems to work:

scala> global.findMemberFromRoot("scala.collection.TraversableLike": TypeName)
res0: $r.global.Symbol = trait TraversableLike

It isn't going to work with "Option[Int]", you're on your own there. There's no general purpose String => Type facility. I'd be thrilled if there were one. I'd be even more thrilled if even the "easy" direction worked all the time, i.e. that the toString of a Type was always valid scala source representing that type, except when impossible to express.

Contributor

paulp commented Apr 5, 2013

There are too many ways to do this sort of thing, but long ago (before reflection) I put this in global, which still seems to work:

scala> global.findMemberFromRoot("scala.collection.TraversableLike": TypeName)
res0: $r.global.Symbol = trait TraversableLike

It isn't going to work with "Option[Int]", you're on your own there. There's no general purpose String => Type facility. I'd be thrilled if there were one. I'd be even more thrilled if even the "easy" direction worked all the time, i.e. that the toString of a Type was always valid scala source representing that type, except when impossible to express.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Apr 5, 2013

Member

If you don't mind I'm going to try to replace my hack logic with your inferKind this weekend.

That would be the ideal outcome of my manipulative reference to that old branch.
There's already a Kinds.scala in the compiler, by the way.

Member

adriaanm commented Apr 5, 2013

If you don't mind I'm going to try to replace my hack logic with your inferKind this weekend.

That would be the ideal outcome of my manipulative reference to that old branch.
There's already a Kinds.scala in the compiler, by the way.

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 5, 2013

Member

@paulp I guess if I had the full name I could ask g.rootMirror too:

scala> g.rootMirror.staticClass("scalaz.Monad")
res3: g.ClassSymbol = class Monad

But now it won't work for imported names:

scala> import scalaz._
import scalaz._

scala> g.rootMirror.staticClass("Monad")
scala.reflect.internal.MissingRequirementError: class Monad not found.
Member

eed3si9n commented Apr 5, 2013

@paulp I guess if I had the full name I could ask g.rootMirror too:

scala> g.rootMirror.staticClass("scalaz.Monad")
res3: g.ClassSymbol = class Monad

But now it won't work for imported names:

scala> import scalaz._
import scalaz._

scala> g.rootMirror.staticClass("Monad")
scala.reflect.internal.MissingRequirementError: class Monad not found.
@folone

This comment has been minimized.

Show comment
Hide comment
@folone

folone Apr 5, 2013

Contributor

@eed3si9n this problem is exactly why I added two cases as a workaround here. This is how I get the fully qualified name for your case.

Contributor

folone commented Apr 5, 2013

@eed3si9n this problem is exactly why I added two cases as a workaround here. This is how I get the fully qualified name for your case.

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 5, 2013

Member

@folone By using exprTyper.typeOfExpression(expr.trim) it's treating the name as a term, so it currently requires companion object for imported names to work?:

scala> class Foo[A]
defined class Foo

scala> :k Foo
<console>:40: error: not found: value Foo
              Foo
              ^

scala> case class Bar[A]()
defined class Bar

scala> :k Bar
Bar's kind is F[A]
Member

eed3si9n commented Apr 5, 2013

@folone By using exprTyper.typeOfExpression(expr.trim) it's treating the name as a term, so it currently requires companion object for imported names to work?:

scala> class Foo[A]
defined class Foo

scala> :k Foo
<console>:40: error: not found: value Foo
              Foo
              ^

scala> case class Bar[A]()
defined class Bar

scala> :k Bar
Bar's kind is F[A]
@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 8, 2013

Member

PLS REBUILD ALL

inferKind is ported. This solves kind with an elegant if statement:

if(!tpe.isHigherKinded)
  ProperTypeKind(tpe.asSeenFrom(pre, owner).bounds, tpe)
else { ... }

Thanks @retronym for solving the ClassInfoType mystery.

The verbose output now includes enhanced standard notation with bounds and variance:

scala> :k -v Either
scala.util.Either's kind is F[+A, +B]
* -(+)-> * -(+)-> *
This is a type constructor: a 1st-order-kinded type.

scala> :k -v scala.collection.generic.Sorted
scala.collection.generic.Sorted's kind is F[K, +This <: Sorted]
* -> *(Sorted) -(+)-> *
This is a type constructor: a 1st-order-kinded type.

I also added a limited String => Type facility. This allows me to write several types that was not supported previously:

scala> class Foo
defined class Foo

scala> :k Foo
Foo's kind is A

scala> :k Int => Int
scala.Function1's kind is F[-T1, +R]
Member

eed3si9n commented Apr 8, 2013

PLS REBUILD ALL

inferKind is ported. This solves kind with an elegant if statement:

if(!tpe.isHigherKinded)
  ProperTypeKind(tpe.asSeenFrom(pre, owner).bounds, tpe)
else { ... }

Thanks @retronym for solving the ClassInfoType mystery.

The verbose output now includes enhanced standard notation with bounds and variance:

scala> :k -v Either
scala.util.Either's kind is F[+A, +B]
* -(+)-> * -(+)-> *
This is a type constructor: a 1st-order-kinded type.

scala> :k -v scala.collection.generic.Sorted
scala.collection.generic.Sorted's kind is F[K, +This <: Sorted]
* -> *(Sorted) -(+)-> *
This is a type constructor: a 1st-order-kinded type.

I also added a limited String => Type facility. This allows me to write several types that was not supported previously:

scala> class Foo
defined class Foo

scala> :k Foo
Foo's kind is A

scala> :k Int => Int
scala.Function1's kind is F[-T1, +R]
@scala-jenkins

This comment has been minimized.

Show comment
Hide comment
@scala-jenkins

scala-jenkins Apr 8, 2013

(kitty-note-to-self: ignore 16032252)
🐱 Roger! Rebuilding pr-rangepos-per-commit, pr-checkin-per-commit for 7855b08f, c9314fb8, c4245ae6, ca9edc14, 4cc2222a, 3245d85e, 8999a60b, 5d7e1431. 🚨

scala-jenkins commented Apr 8, 2013

(kitty-note-to-self: ignore 16032252)
🐱 Roger! Rebuilding pr-rangepos-per-commit, pr-checkin-per-commit for 7855b08f, c9314fb8, c4245ae6, ca9edc14, 4cc2222a, 3245d85e, 8999a60b, 5d7e1431. 🚨

@ghost ghost assigned adriaanm Apr 10, 2013

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 13, 2013

Member

@adriaanm I took the type out of the kind. I was ignoring bounds that's the same as tpe, so now it comes out like this for proper types:

scala> :k Int
scala.Int's kind is A >: Int <: Int

scala> class Foo
defined class Foo

scala> :k Foo
Foo's kind is A >: Foo <: Foo

I don't know if this is correct/helpful.

Member

eed3si9n commented Apr 13, 2013

@adriaanm I took the type out of the kind. I was ignoring bounds that's the same as tpe, so now it comes out like this for proper types:

scala> :k Int
scala.Int's kind is A >: Int <: Int

scala> class Foo
defined class Foo

scala> :k Foo
Foo's kind is A >: Foo <: Foo

I don't know if this is correct/helpful.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Apr 18, 2013

Member

sorry, i got swamped and lost track of this

I'd expect Int and Foo's kind to simply be *
I don't think bounds are relevant for concrete types. For abstract types, they are, of course.

type X <: String would have kind *(Nothing, String), or *(String) since we usually only use upper bounds

Member

adriaanm commented Apr 18, 2013

sorry, i got swamped and lost track of this

I'd expect Int and Foo's kind to simply be *
I don't think bounds are relevant for concrete types. For abstract types, they are, of course.

type X <: String would have kind *(Nothing, String), or *(String) since we usually only use upper bounds

@paulp

This comment has been minimized.

Show comment
Hide comment
@paulp

paulp Apr 19, 2013

Contributor

I also expect Int and Foo's kind to be *, if anyone was waiting with bated breath for me to chime in.

Contributor

paulp commented Apr 19, 2013

I also expect Int and Foo's kind to be *, if anyone was waiting with bated breath for me to chime in.

@paulp

View changes

Show outdated Hide outdated src/reflect/scala/reflect/internal/Kinds.scala
@paulp

View changes

Show outdated Hide outdated src/reflect/scala/reflect/internal/Kinds.scala
@paulp

View changes

Show outdated Hide outdated src/reflect/scala/reflect/internal/Kinds.scala
@paulp

View changes

Show outdated Hide outdated src/reflect/scala/reflect/internal/Kinds.scala
@paulp

View changes

Show outdated Hide outdated src/reflect/scala/reflect/internal/Kinds.scala
@paulp

View changes

Show outdated Hide outdated src/reflect/scala/reflect/internal/Kinds.scala
Add :kind command to REPL
:kind command diplays the kind of types and type constructors in Scala
syntax notation.

    scala> :kind (Int, Int) => Int
    scala.Function2's kind is F[-A1,-A2,+A3]
@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Apr 21, 2013

Member

Added a new commit reflecting @paulp's code review. I've accepted all points that were raised.

Mainly, Kind now tries to avoid getting into the business of converting other objects (Variance, Type, and TypeBounds) into String, and instead call the appropriate method on them to do the conversion. For that purpose, scalaNotation and starNotation methods were added to TypeBounds. Also, the logic that was passing a mutable map around to build Scala notation is replaced with series of immutable state transformation.

Other changes:

  • Proper types no longer contains bounds (Int's kind is A)
  • Type constructors now contains bounds too (scala.collection.generic.ImmutableSortedMapFactory's kind is X[CC[A,B] <: scala.collection.immutable.SortedMap[A,B] with scala.collection.SortedMapLike[A,B,CC[A,B]]])
  • Type variable names are used if bounds exists, otherwise they are normalized to prescribed alphabets: A, F, X, Y, Z...

The commits were growing, so I squashed them and rebased it off the current master.

Member

eed3si9n commented Apr 21, 2013

Added a new commit reflecting @paulp's code review. I've accepted all points that were raised.

Mainly, Kind now tries to avoid getting into the business of converting other objects (Variance, Type, and TypeBounds) into String, and instead call the appropriate method on them to do the conversion. For that purpose, scalaNotation and starNotation methods were added to TypeBounds. Also, the logic that was passing a mutable map around to build Scala notation is replaced with series of immutable state transformation.

Other changes:

  • Proper types no longer contains bounds (Int's kind is A)
  • Type constructors now contains bounds too (scala.collection.generic.ImmutableSortedMapFactory's kind is X[CC[A,B] <: scala.collection.immutable.SortedMap[A,B] with scala.collection.SortedMapLike[A,B,CC[A,B]]])
  • Type variable names are used if bounds exists, otherwise they are normalized to prescribed alphabets: A, F, X, Y, Z...

The commits were growing, so I squashed them and rebased it off the current master.

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n May 17, 2013

Member

Any chance :kind gets into 2.11.0-M3?

Member

eed3si9n commented May 17, 2013

Any chance :kind gets into 2.11.0-M3?

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm May 17, 2013

Member

LGTM

Thanks for patiently slogging through the PR maze. There are some refinements to be made before using the Kinds data structure more generally, but let's get this in already!

(Here's my -- very late -- review:

  • only proper kinds track bounds
  • tycon kinds track them indirectly by the kinds they compose into function kinds
  • also, I prefer "parameter" to "argument" when referring to the abstraction and not the application
    )
Member

adriaanm commented May 17, 2013

LGTM

Thanks for patiently slogging through the PR maze. There are some refinements to be made before using the Kinds data structure more generally, but let's get this in already!

(Here's my -- very late -- review:

  • only proper kinds track bounds
  • tycon kinds track them indirectly by the kinds they compose into function kinds
  • also, I prefer "parameter" to "argument" when referring to the abstraction and not the application
    )

adriaanm added a commit that referenced this pull request May 17, 2013

Merge pull request #2340 from folone/topic/kind-pr
Added a :kind command to the REPL

@adriaanm adriaanm merged commit bf3c44c into scala:master May 17, 2013

1 check passed

default pr-checkin-per-commit Took 56 min.
Details
@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n May 17, 2013

Member

Nice! Thanks all for the feedback.

Member

eed3si9n commented May 17, 2013

Nice! Thanks all for the feedback.

eed3si9n added a commit to eed3si9n/scala that referenced this pull request May 20, 2017

Improve :kind by only accepting type expressions
Fixes scala/bug#8529

The :kind implementation added in scala/scala#2340 was buggy and misleading in part because we could not find a robust way to turning type expression strings into Type. See also http://stackoverflow.com/questions/15678616/getting-type-information-inside-scala-repl-via-imain/15694003#15694003. The problem was that we didn't know how to pick up types defined in the REPL, so #2340 faked it by treating the expression as a term.

That's the major source of problems reported in scala/bug#8529. This change improves the String to type symbol conversion by using `exprTyper.typeOfTypeString` repeatedly with `[Nothing]`. It's hacky, but a whole class of "expression" can now be called out as not being a type.

Before:

    scala> :kind 5
    scala.Int's kind is A

After:

    scala> :kind 5
    <console>:1: error: identifier expected but integer literal found.
    def $ires23: 5 = ???
                 ^

More importantly, it will no longer mix up companion objects with actual types.

Before:

    scala> :kind -v Predef.Pair
    scala.Predef.Pair's kind is A
    *
    This is a proper type.

After:

    scala> :kind -v Predef.Pair
    scala.Tuple2's kind is F[+A1,+A2]
    * -(+)-> * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.

eed3si9n added a commit to eed3si9n/scala that referenced this pull request May 20, 2017

Improve :kind by only accepting type expressions
Fixes scala/bug#8529

The :kind implementation added in scala/scala#2340 was buggy and misleading in part because we could not find a robust way to turn a type expression string into a Type. See also [Getting type information inside scala repl via IMain](http://stackoverflow.com/questions/15678616/getting-type-information-inside-scala-repl-via-imain/15694003#15694003). The problem was that we didn't know how to pick up types defined or imported in the REPL, so #2340 faked it by treating the given type expression as a term.

That's the major source of problems reported in scala/bug#8529. This commit improves the String to type symbol conversion by using `exprTyper.typeOfTypeString` repeatedly with `[Nothing]`. It's hacky, but a whole class of "expression" can now be called out as not being a type.

Before:

    scala> :kind 5
    scala.Int's kind is A

After:

    scala> :kind 5
    <console>:1: error: identifier expected but integer literal found.
    def $ires23: 5 = ???
                 ^

More importantly, it will no longer mix up companion objects with actual types.

Before:

    scala> :kind -v Predef.Pair
    scala.Predef.Pair's kind is A
    *
    This is a proper type.

After:

    scala> :kind -v Predef.Pair
    scala.Tuple2's kind is F[+A1,+A2]
    * -(+)-> * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.

eed3si9n added a commit to eed3si9n/scala that referenced this pull request May 20, 2017

Improve :kind by only accepting type expressions
Fixes scala/bug#8529

The :kind implementation added in scala/scala#2340 was buggy and misleading in part because we could not find a robust way to turn a type expression string into a Type. See also [Getting type information inside scala repl via IMain](http://stackoverflow.com/questions/15678616/getting-type-information-inside-scala-repl-via-imain/15694003#15694003). The problem was that we didn't know how to pick up types defined or imported in the REPL, so #2340 faked it by treating the given type expression as a term.

That's the major source of problems reported in scala/bug#8529. This commit improves the String to type symbol conversion by using `exprTyper.typeOfTypeString` repeatedly with `[Nothing]`. It's hacky, but a whole class of "expression" can now be called out as not being a type.

Before:

    scala> :kind 5
    scala.Int's kind is A

After:

    scala> :kind 5
    <console>:1: error: identifier expected but integer literal found.
    def $ires23: 5 = ???
                 ^

More importantly, it will no longer mix up companion objects with actual types.

Before:

    scala> :kind -v Predef.Pair
    scala.Predef.Pair's kind is A
    *
    This is a proper type.

After:

    scala> :kind -v Predef.Pair
    scala.Tuple2's kind is F[+A1,+A2]
    * -(+)-> * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.

eed3si9n added a commit to eed3si9n/scala that referenced this pull request May 21, 2017

Improve :kind by only accepting type expressions
Fixes scala/bug#8529

The :kind implementation added in scala/scala#2340 was buggy and misleading in part because we could not find a robust way to turn a type expression string into a Type. See also [Getting type information inside scala repl via IMain](http://stackoverflow.com/questions/15678616/getting-type-information-inside-scala-repl-via-imain/15694003#15694003). The problem was that we didn't know how to pick up types defined or imported in the REPL, so #2340 faked it by treating the given type expression as a term.

That's the major source of problems reported in scala/bug#8529. This commit improves the String to type symbol conversion by using `exprTyper.typeOfTypeString` repeatedly with `[Nothing]`. It's hacky, but a whole class of "expression" can now be called out as not being a type.

Before:

    scala> :kind 5
    scala.Int's kind is A

After:

    scala> :kind 5
    <console>:1: error: identifier expected but integer literal found.
    def $ires23: 5 = ???
                 ^

More importantly, it will no longer mix up companion objects with actual types.

Before:

    scala> :kind -v Predef.Pair
    scala.Predef.Pair's kind is A
    *
    This is a proper type.

After:

    scala> :kind -v Predef.Pair
    scala.Tuple2's kind is F[+A1,+A2]
    * -(+)-> * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.

eed3si9n added a commit to eed3si9n/scala that referenced this pull request May 21, 2017

Improve :kind by only accepting type expressions
Fixes scala/bug#8529

The :kind implementation added in scala/scala#2340 was buggy and misleading in part because we could not find a robust way to turn a type expression string into a Type. See also [Getting type information inside scala repl via IMain](http://stackoverflow.com/questions/15678616/getting-type-information-inside-scala-repl-via-imain/15694003#15694003). The problem was that we didn't know how to pick up types defined or imported in the REPL, so #2340 faked it by treating the given type expression as a term.

That's the major source of problems reported in scala/bug#8529. This commit improves the String to type symbol conversion by using `exprTyper.typeOfTypeString` repeatedly with `[Nothing]`. It's hacky, but a whole class of "expression" can now be called out as not being a type.

Before:

    scala> :kind 5
    scala.Int's kind is A

After:

    scala> :kind 5
    <console>:1: error: identifier expected but integer literal found.
    def $ires23: 5 = ???
                 ^

More importantly, it will no longer mix up companion objects with actual types.

Before:

    scala> :kind -v Predef.Pair
    scala.Predef.Pair's kind is A
    *
    This is a proper type.

After:

    scala> :kind -v Predef.Pair
    scala.Tuple2's kind is F[+A1,+A2]
    * -(+)-> * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment