12

Update (2018): my prayers were answered in Dotty (Type Lambdas), so the following Q&A is more "Scala 2.x"-related


Just a simple example from Scala:

scala> def f(x: Int) = x
f: (x: Int)Int

scala> (f _)(5)
res0: Int = 5

Let's make it generic:

scala> def f[T](x: T) = x
f: [T](x: T)T

scala> (f _)(5)
<console>:9: error: type mismatch;
 found   : Int(5)
 required: Nothing
              (f _)(5)
                    ^

Let's look at eta-expansion of polymorphic method in Scala:

scala> f _ 
res2: Nothing => Nothing = <function1>

Comparison with Haskell:

Prelude> let f x = x

Prelude> f 5
5
Prelude> f "a"
"a"
Prelude> :t f
f :: t -> t

Haskell did infer correct type [T] => [T] here.

More realistic example?

scala> identity _
res2: Nothing => Nothing = <function1>

Even more realistic:

scala> def f[T](l: List[T]) = l.head
f: [T](l: List[T])T

scala> f _
res3: List[Nothing] => Nothing = <function1>

You can't make alias for identity - have to write your own function. Things like [T,U](t: T, u: U) => t -> u (make tuple) are impossible to use as values. More general - if you want to pass some lambda that rely on generic type (e.g. uses generic function, for example: creates lists, tuples, modify them in some way) - you can't do that.

So, how to solve that problem? Any workaround, solution or reasoning?

P.S. I've used term polymorphic lambda (instead of function) as function is just named lambda

dk14
  • 22,206
  • 4
  • 51
  • 88
  • @som-snytt `Poly` seems like good workaround, so I have to wait for a good syntax for it (to actually see types inside `Poly` object and have eta-expansion). I mean `Shapeless` could define eta-expansion to `Poly` to have nicer syntax. Thanks anyway. – dk14 Jul 21 '15 at 05:22
  • @dk15 Eta expansion in Scala is a kind of kludgy bridge between methods and functions—it doesn't really make sense for Shapeless's polymorphic function values. – Travis Brown Jul 21 '15 at 05:25
  • @Travis Brown - I mean expand method to `Poly` instead of `Function`. So I need convenient bridge to `Shapeless`'s poly-function instead of scala's `Function` – dk14 Jul 21 '15 at 05:27
  • 2
    @dk14 Ah, got it. `Poly(identity _)`, etc. will work in some cases. – Travis Brown Jul 21 '15 at 05:37
  • `val a: (Int) => Int = f _; a(4)` ? – Jatin Jul 21 '15 at 09:35
  • @Travis Brown - that's what I need - I used to write `Poly` functions by myself (implementing `Poly1` etc) - now my life (in some cases) became easier. Thanks! – dk14 Jul 21 '15 at 10:04

4 Answers4

7

Only methods can be generic on the JVM/Scala, not values. You can make an anonymous instance that implements some interface (and duplicate it for every type-arity you want to work with):

trait ~>[A[_], B[_]] { //exists in scalaz
  def apply[T](a: A[T]): B[T]
}

val f = new (List ~> Id) {
  def apply[T](a: List[T]) = a.head
}

Or use shapeless' Poly, which supports more complicated type-cases. But yeah, it's a limitation and it requires working around.

lmm
  • 17,386
  • 3
  • 26
  • 37
  • 2
    it's not much related to the JVM actually as it happens in compile-time (regular `Function` is also may be seen workaround). I agree that for now `Poly` seems like best solution. – dk14 Jul 21 '15 at 09:56
2

P∀scal is a compiler plugin that provides more concise syntax for encoding polymorphic values as objects with a generic method.

The identity function, as a value, has type ∀A. A => A. To translate that into Scala, assume a trait

trait ForAll[F[_]] {
  def apply[A]: F[A]
}

Then the identity function has type ForAll[λ[A => A => A]], where I use the kind-projector syntax, or, without kind-projector:

type IdFun[A] = A => A
type PolyId = ForAll[IdFun]

And now comes the P∀scal syntactic sugar:

val id = Λ[Α](a => a) : PolyId

or equivalently

val id = ν[PolyId](a => a)

("ν" is the Greek lowercase letter "Nu", read "new")

These are really just shorthands for

new PolyId {
  def apply[A] = a => a
}

Multiple type parameters and parameters of arbitrary kinds are supported by P∀scal, but you need a dedicated variation on the above ForAll trait for each variant.

Tomas Mikula
  • 6,537
  • 25
  • 39
  • That's really nice! I'd mention that with kind-projector ([typelevel](https://github.com/non/kind-projector) plugin), we (probably, didn't try yet) could just write: `type PolyId = ForAll[Lambda[A => A]]` or even `val id = ν[ForAll[Lambda[A => A]]](a => a)`. Maybe, you could add some interoperabilitty shortcut (it could be in a separate project to keep your solution free from unnecessary dependencies) as well like `val id = poly[A => A](a => a)` – dk14 Aug 10 '17 at 15:30
  • For `poly[A => A](a => a)`, we would need to determine and _hard-code_ what trait to implement (here, `ForAll`). So the plugin would have to come with a library containing a lot of variants on `ForAll[F[_]]`, like `ForAll2[F[_, _]]`, `ForAllH[F[_[_]]]`, `ForAllHA[F[_[_]], G[_]]`, ... Another option would be to use structural types, which according to some [are a non-starter](https://github.com/non/kind-projector/pull/44#issuecomment-267750484). – Tomas Mikula Aug 10 '17 at 15:40
  • I agree this kind of library would look "ugly"; however, as a separate (pragmatic) project, why not? even let's say shapeless has its limits on `nat`s and every-one ok with it. Besides, you could just generate those types with a macros (you can add custom expansion for Intellij IDEA). Anyways for one plane parameter this one-liner works (Scala 2.12, given that `ForAll` is defined): `val id = Λ[a](a => a) : ForAll[Lambda[a => a => a]]`, which is really cool :) – dk14 Aug 10 '17 at 16:20
  • also (just for people who read comments here) `val toList = Λ[a](x => List(x)) : ForAll[Lambda[a => a => List[a]]]`. `toList.apply(5)` - scala forced me to explicitly call `apply` for some reason – dk14 Aug 10 '17 at 16:20
  • You needed at least one explicit `apply`, because it is actually two `apply`s in a row: `toList.apply[Int].apply(5)`. You could also write it as `toList[Int](5)`. This special case when the `F[_]` in `ForAll[F]` is a function type could have some additional syntactic convenience, but that's up to the library. – Tomas Mikula Aug 10 '17 at 17:25
  • > "however, as a separate (pragmatic) project, why not?" Separate from what? It cannot be separate from the plugin. – Tomas Mikula Aug 10 '17 at 17:25
  • 1
    you can generate/hardcode convinience ForAll stuff in a completely separate project as they don't require any plugin at all. Shortcuts are more complicated (and maybe it would be a hacky solution), but they still could be a separate plugin that is using your plugin as a library (plugin is essentialy a jar with implemention of "Plugin" interface after all - even kind-projector's KindRewriter is formally a public class) – dk14 Aug 10 '17 at 19:06
1

I really like @Travis Brown 's solution:

import shapeless._

scala> Poly(identity _)
res2: shapeless.PolyDefns.~>[shapeless.Id,shapeless.Id] = fresh$macro$1$2$@797aa352

-

scala> def f[T](x: T) = x
f: [T](x: T)T

scala> Poly(f _)
res3: shapeless.PolyDefns.~>[shapeless.Id,shapeless.Id] = fresh$macro$2$2$@664ea816

-

scala> def f[T](l: List[T]) = l.head
f: [T](l: List[T])T

scala> val ff = Poly(f _)
ff: shapeless.PolyDefns.~>[List,shapeless.Id] = fresh$macro$3$2$@51254c50

scala> ff(List(1,2,3))
res5: shapeless.Id[Int] = 1

scala> ff(List("1","2","3"))
res6: shapeless.Id[String] = 1

Poly constructor (in some cases) will give you eta-expansion into Shapeless2 Poly1 function, which is (more-less) truly generic. However it doesn't work for multi-parameters (even with multi type-parameters), so have to "implement" Poly2 with implicit + at approach (as @som-snytt suggested), something like:

object myF extends Poly2 {
  implicit def caseA[T, U] = at[T, U]{ (a, b) => a -> b}
}

scala> myF(1,2)
res15: (Int, Int) = (1,2)

scala> myF("a",2)
res16: (String, Int) = (a,2)

P.S. I would really want to see it as a part of language.

dk14
  • 22,206
  • 4
  • 51
  • 88
  • I think we all would, but it's probably too big a change to happen in Scala at this stage. You might look to e.g. Idris for a language that takes some inspiration from Scala, but has more support for type-level programming. – lmm Jul 21 '15 at 10:51
0

It seems to do this you will need to do a bit type hinting to help the Scala type inference system.

def id[T] : T => T = identity _

So I guess if you try to pass identity as a parameter to a function call and the types of that parameter are generic then there should be no problem.

Ankur
  • 33,367
  • 2
  • 46
  • 72
  • It's still not a value - there is no way to pass it into another function in generic way. Simply saying, you've defined a method here - not a function – dk14 Jul 21 '15 at 05:14
  • See http://stackoverflow.com/questions/2529184/difference-between-method-and-function-in-scala – dk14 Jul 21 '15 at 05:16
  • I mean `id _` is still `Nothing => Nothing`. I can use it as workaround for aliases, but not as generic solution. – dk14 Jul 21 '15 at 05:17
  • Have a look at: http://stackoverflow.com/questions/15264838/in-scala-can-generic-type-parameters-be-used-with-function-definitions – Ankur Jul 21 '15 at 05:21
  • Ok, my question is a bit duplicate, however I'm looking for any generic solution to that problem - as now we have macro and Shapeless - maybe someone created library that can do that. – dk14 Jul 21 '15 at 05:26