type currying

203 views
Skip to first unread message

Paul Phillips

unread,
Dec 26, 2011, 8:48:33 PM12/26/11
to scala-i...@googlegroups.com
I hacked on the parser just a very little bit to make this possible.  The changes are little more than stopping it from gratuitously failing on back-to-back bracketed lists.


import scala.collection.{ mutable, immutable, generic }
import generic.CanBuildFrom

object Partial {
  type KnownContainer[CC[K, V] <: collection.Map[K, V]] = {
    def values[V] : KnownValues[CC, V]
    def apply[K] : KnownKeys[CC, K]
  }
  type KnownKeys[CC[K, V] <: collection.Map[K, V], K] = {
    def apply[V](implicit cbf: CanBuildFrom[_, (K, V), CC[K, V]]): CC[K, V]
  }
  type KnownValues[CC[K, V] <: collection.Map[K, V], V] = {
    def apply[K](implicit cbf: CanBuildFrom[_, (K, V), CC[K, V]]): CC[K, V]
  }

  def apply[CC[K, V] <: collection.Map[K, V]] : KnownContainer[CC] = new {
    def values[V] : KnownValues[CC, V] = new {
      def apply[K](implicit cbf: CanBuildFrom[_, (K, V), CC[K, V]]) = cbf().result
    }
    def apply[K] = new {
      def apply[V](implicit cbf: CanBuildFrom[_, (K, V), CC[K, V]]) = cbf().result
    }
  }
}

scala> val m = Partial[immutable.TreeMap]
m: Partial.KnownContainer[scala.collection.immutable.TreeMap] = Partial$$anon$1@20beaacc

scala> val m1 = m[String]
m1: Partial.KnownKeys[scala.collection.immutable.TreeMap,String] = Partial$$anon$1$$anon$2@13979163

scala> val m2 = m[Int][Int]
m2: scala.collection.immutable.TreeMap[Int,Int] = Map()

scala> val mutableBippy = Partial[mutable.HashMap][String][Int]
mutableBippy: scala.collection.mutable.HashMap[String,Int] = Map()

scala> mutableBippy("abc") = 55

scala> println(mutableBippy)
Map(abc -> 55)

scala> val immutableBippy = Partial[immutable.HashMap].values[Int]
immutableBippy: Partial.KnownValues[scala.collection.immutable.HashMap,Int] = Partial$$anon$1$$anon$3@6d02f1ee

scala> def make[T](xs: T*) = immutableBippy[T] ++ xs.zipWithIndex
make: [T](xs: T*)scala.collection.immutable.Map[T,Int]

scala> make('a' to 'z': _*)
res7: scala.collection.immutable.Map[Char,Int] = Map(e -> 4, s -> 18, x -> 23, n -> 13, j -> 9, y -> 24, t -> 19, u -> 20, f -> 5, a -> 0, m -> 12, i -> 8, v -> 21, q -> 16, b -> 1, g -> 6, l -> 11, p -> 15, c -> 2, h -> 7, r -> 17, w -> 22, k -> 10, o -> 14, z -> 25, d -> 3)

Miles Sabin

unread,
Dec 27, 2011, 5:14:07 AM12/27/11
to scala-i...@googlegroups.com
On Tue, Dec 27, 2011 at 1:48 AM, Paul Phillips <pa...@improving.org> wrote:
> I hacked on the parser just a very little bit to make this possible.  The
> changes are little more than stopping it from gratuitously failing on
> back-to-back bracketed lists.

Is the patch available anywhere?

Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://www.chuusai.com/

Anwar Rizal

unread,
Dec 27, 2011, 6:03:32 AM12/27/11
to scala-i...@googlegroups.com
Not sure to understand this.

Does this mean that we can now avoid 

({type L[a]=Either[String, a]})#L[A] 


Paul Phillips

unread,
Dec 27, 2011, 9:52:07 AM12/27/11
to scala-i...@googlegroups.com

On Tue, Dec 27, 2011 at 2:14 AM, Miles Sabin <mi...@milessabin.com> wrote:
Is the patch available anywhere?

No, but it's small enough to send by carrier pigeon.


diff --git c/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala i/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index e27d5cacda..00ac3976a9 100644
--- c/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ i/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1534,9 +1534,10 @@ self =>
           val t1 = stripParens(t)
           t1 match {
             case Ident(_) | Select(_, _) =>
-              val tapp = atPos(t1.pos.startOrPoint, in.offset) {
-                TypeApply(t1, exprTypeArgs())
-              }
+              var tapp: Tree = t1
+              while (in.token == LBRACKET)
+                tapp = atPos(tapp.pos.startOrPoint, in.offset)(TypeApply(tapp, exprTypeArgs()))
+
               simpleExprRest(tapp, true)
             case _ =>
               t1

Paul Phillips

unread,
Dec 27, 2011, 9:56:51 AM12/27/11
to scala-i...@googlegroups.com
Also, I've convinced myself that this is a bugfix, so unless martin speaks up otherwise I'll commit it.

   A type application e[T[1],…,T[n]] ...

   If the function part e is of some value type, the type application is
   taken to be equivalent to  e.apply[T[1],…, T[n]], i.e. the application of
   an apply method defined by e.

Oh my god it's so nice to finally be able to paste from the spec.  Thanks again Erik.

Julien Richard-Foy

unread,
Dec 27, 2011, 10:33:30 AM12/27/11
to scala-i...@googlegroups.com
Maybe type currying should be explicit, as partial function application is, in order to avoid people to write code they did not intended to write?

E.g. something like the following:

val m1 = m[String] _

Paul Phillips

unread,
Dec 27, 2011, 10:47:58 AM12/27/11
to scala-i...@googlegroups.com


On Tue, Dec 27, 2011 at 7:33 AM, Julien Richard-Foy <juli...@gmail.com> wrote:
Maybe type currying should be explicit, as partial function application is, in order to avoid people to write code they did not intended to write?

This is really a stretch.  First, what else would they be intending? Secondly, partial function application works exactly the same in the analogous context.

scala>   object Foo {
     |     def apply(x: Int) = new {
     |       def apply(y: Int) = x + y
     |     }
     |   }
defined module Foo

scala> Foo(5)(10)
res0: Int = 15

scala> Foo(5)
res1: Object{def apply(y: Int): Int} = Foo$$anon$1@4be0ef94

Paolo G. Giarrusso

unread,
Jul 26, 2012, 1:10:50 PM7/26/12
to scala-i...@googlegroups.com


Il giorno martedì 27 dicembre 2011 16:47:58 UTC+1, Paul Phillips ha scritto:


On Tue, Dec 27, 2011 at 7:33 AM, Julien Richard-Foy <juli...@gmail.com> wrote:
Maybe type currying should be explicit, as partial function application is, in order to avoid people to write code they did not intended to write?

This is really a stretch.  First, what else would they be intending?
It looks like an alternative meaning might exist, but I'm not sure. Suppose I have two type parameters and I want some to be deduced, but some to be passed explicitly. Suppose also I have no parameter lists available. Then I need to pass only the first type parameter list, but get another type application automatically done.

However, in the example below, the `that` parameter saved the day. If types in the second parameter list can be deduced, it seems that they can only be deduced from the arguments: what can be deduced from the receiver was already deduced, after all. If I didn't miss anything, the "alternative meaning" does not seem needed, but I'm not sure.

    def myIsInstanceOf[T] = new { //[T: TypeTag] does not give the expected invocation syntax
      def apply[U: TypeTag](that: U)(implicit t: TypeTag[T]) = 
        typeOf[U] <:< typeOf[T]
    }
Btw, for context and alternatives, see my answer here:

In particular, I'm wondering whether this would fit in Predef (code from that answer with some renaming):

import scala.reflect.runtime.universe._
implicit class StatInstanceOf[U: TypeTag](that: U) {
 
def stat_isInstanceOf[T: TypeTag] =
    typeOf
[U] <:< typeOf[T]
}

Paul Phillips

unread,
Jul 26, 2012, 1:28:07 PM7/26/12
to scala-i...@googlegroups.com


On Thu, Jul 26, 2012 at 10:10 AM, Paolo G. Giarrusso <p.gia...@gmail.com> wrote:
In particular, I'm wondering whether this would fit in Predef (code from that answer with some renaming):

We should completely reconsider the naming of:

  classOf
  isInstanceOf
  asInstanceOf

...in light of the existence of ClassTags and TypeTags, which mean that each of those now has at least one variation with different semantics, and we have similar methods like "typeOf" which looks like an obvious peer to "classOf" but is completely different.

I do not believe any set of names can be grafted on which will adequately convey the relationships between these methods.

Paolo Giarrusso

unread,
Jul 26, 2012, 8:27:29 PM7/26/12
to scala-i...@googlegroups.com
On Thu, Jul 26, 2012 at 7:28 PM, Paul Phillips <pa...@improving.org> wrote:
>
>
> On Thu, Jul 26, 2012 at 10:10 AM, Paolo G. Giarrusso <p.gia...@gmail.com>
> wrote:
>>
>> In particular, I'm wondering whether this would fit in Predef (code from
>> that answer with some renaming):
>
>
> We should completely reconsider the naming of:
>
> classOf
> isInstanceOf
> asInstanceOf
>
> ...in light of the existence of ClassTags and TypeTags, which mean that each
> of those now has at least one variation with different semantics, and we
> have similar methods like "typeOf" which looks like an obvious peer to
> "classOf" but is completely different.

I agree with your point, especially in an ideal world. But if you
propose renaming them before the proposed non-compatible Scala 3.0
(from Martin's "Scala - a Roadmap"), I think that it's too late—that
risks breaking too much code. A deprecation cycle could help,
refactoring tools would help more, but this sounds too late.

> I do not believe any set of names can be grafted on which will adequately
> convey the relationships between these methods.

But that begs the question: if no adequate names really exist, is it
worth changing them? The obvious answer sounds like "but there might
be something better". I'm going to shoot for that.

With all this disclaimers, if you drop the implicit brevity
constraint, I'd start by calling

class-based isInstanceOf -> isDynamicallyErasedInstanceOf, maybe
together with the current name as an alias.
TypeTag-based isInstanceOf -> isStaticallyNonErasedInstanceOf.

Am I missing more differences between the two to reflect in the names?

I'm confused by the "-ally" suffix there, since I have the impression
adjective-adverb distinctions are usually dropped in names
(counterexamples are appreciated), but "is statically an instance"
makes much, much more sense than "is a static instance".

These names should also convey the fact that none of them is better
than the other (which I missed for a while while writing the new
isInstanceOf). In particular, assuming an orthogonal design space,
nonErasedInstanceOf implies erasedInstanceOf, but
dynamicallyInstanceOf implies staticallyInstanceOf. In fact, the
design space is not so orthogonal since types are erased,
so the theoretically perfect isDynamicallyNonErasedInstanceOf can't be
implemented.
but there's space for isStaticallyErasedInstanceOf, which is
(somewhat) useful to understand why the other two disagree when they
do, I think.

We have the implication chain isStaticallyNonErasedInstanceOf =>
isStaticallyErasedInstanceOf => isDynamicallyErasedInstanceOf; it
follows that isStaticallyErasedInstanceOf is the closest approximation
of isDynamicallyErasedInstanceOf. Although you need this when you're
playing evil tricks, these tricks are often needed (and used even in
the standard library - to wit, see uncheckedVariance).

However, I don't see an easy way to erase a TypeTag to the
corresponding existential to implement isStaticallyErasedInstanceOf.
I'm sure it's possible since the compiler does it, but I'd like a
method in TypeTag for it.

This also begs the question: why does a TypeTag _not_ contain a
ClassTag or at least a Class? Writing [T: ClassTag: TypeTag] is not
fun. Worst-case, any proper type can be erased to Any, and you don't
have (directly) values of type TypeTag[T] if T is a type constructor.

Best regards
--
Paolo Giarrusso - Ph.D. Student, Philipps-University Marburg
http://www.informatik.uni-marburg.de/~pgiarrusso/

Eugene Burmako

unread,
Jul 27, 2012, 1:56:56 AM7/27/12
to scala-i...@googlegroups.com
Changing a TypeTag to not incude an erasure (TypeTag used to subclass ClassTag in M3) was a conscious design decision. TypeTag and ClassTag represent different concepts (and some of those concepts might be or not be available depending on a platform).

As to verbosity, once we fix implicit macros (most likely not in 2.10.0) it will be possible to write your own "implicit def materializeYourTag[T]: YourTag[T]" macro that summons a data structure that combines a type tag and a class tag

Alex Cruise

unread,
Jul 27, 2012, 12:59:41 PM7/27/12
to scala-i...@googlegroups.com
On Thu, Jul 26, 2012 at 10:56 PM, Eugene Burmako <eugene....@epfl.ch> wrote:
> Changing a TypeTag to not incude an erasure

*perk*

Can I humbly ask you to fill in one or two rows of
https://wiki.scala-lang.org/display/SW/2.10+Reflection+and+Manifest+Migration+Notes
-- or point to something else with broadly similar goals? :)

Thanks!

-0xe1a

Paul Phillips

unread,
Jul 27, 2012, 1:48:15 PM7/27/12
to scala-i...@googlegroups.com
On Thu, Jul 26, 2012 at 5:27 PM, Paolo Giarrusso <pgiar...@mathematik.uni-marburg.de> wrote:

> I do not believe any set of names can be grafted on which will adequately
> convey the relationships between these methods.

But that begs the question: if no adequate names really exist, is it
worth changing them?

Adequate names exist.  "Grafted on" means "new names tacked onto the existing names." The adequate names which exist will not involve identifiers chosen ten or however many years ago when there were no "shades of instance test."

class-based isInstanceOf -> isDynamicallyErasedInstanceOf, maybe
together with the current name as an alias.
TypeTag-based isInstanceOf -> isStaticallyNonErasedInstanceOf.

Am I missing more differences between the two to reflect in the names?

There are more shades of isInstanceOf.

There is "jvm-faithful" isInstanceOf, which means arrays are covariant, because that's what the instanceof opcode says:

  Array[String]("").isInstanceOf[Array[AnyRef]]

And indeed that's true in the current implementation.  Then there's the refined version where we just correct java's wrong answers, which is what will happen with pattern matching whenever it gets implemented.

  (Array[String](""): Any) match { case _: Array[AnyRef] => true ; case _ => false }

That's also true in the current implementation, but will someday be false.

There is the "instance check based on java.lang.Class" and "instance check based on scala.reflect.ClassTag", which are not the same thing, and you can't give up either of them.

And there are more differences between isInstanceOf and pattern matching too (outer pointer checks, etc) and that's all before we even start to consider classtags and typetags.

So lacking any time for this I'll just point it out and forget it.

> Writing [T: ClassTag: TypeTag] is not fun.

It's not my favorite thing to write either but there's nothing stopping you from writing whatever variation of this (which is written to store values but could be written based on types alone of course.)

trait TagWrappers {
  val global: api.Universe
  import global._

  class TaggedValue[T : TypeTag : ClassTag](val value: T) {
    def tag   = typeTag[T]
    def ctag  = classTag[T]

    override def toString = value match {
      case null => "" + typeOf[T]
      case x    => "%s: %s".format(x, typeOf[T])
    }
  }
  implicit def newTaggedValue[T : TypeTag : ClassTag](value: T): TaggedValue[T] = new TaggedValue[T](value)
}

Eugene Burmako

unread,
Aug 5, 2012, 5:00:54 PM8/5/12
to scala-i...@googlegroups.com
Will do that in the near future while fixing https://issues.scala-lang.org/browse/SI-6132
Reply all
Reply to author
Forward
0 new messages