2

I would like to convert an expression such as: a.meth(b) to a function of type (A, B) => C that performs that exact computation.

My best attempt so far was along these lines:

def polish[A, B, C](symb: String): (A, B) => C = { (a, b) =>
// reflectively check if "symb" is a method defined on a
// if so, reflectively call symb, passing b
}

And then use it like this:

def flip[A, B, C](f : (A, B) => C): (B, A) => C = {(b, a) => f(a,b)}
val op = flip(polish("::"))
def reverse[A](l: List[A]): List[A] = l reduceLeft op

As you can pretty much see, it is quite ugly and you have to do a lot of type checking "manually".

Is there an alternative ?

Radu Stoenescu
  • 3,187
  • 9
  • 28
  • 45
  • I don't quite understand how one would use the `polish` method. Can you show an example use case? – Ionuț G. Stan Aug 20 '14 at 09:58
  • but `symb` is a defined as value – goral Aug 20 '14 at 09:59
  • 1
    I got it actually. Something like `polish("charAt")("quux", 2)`. – Ionuț G. Stan Aug 20 '14 at 10:02
  • I would use it like this: l.foldLeft(Nil)(polish("::")) – Radu Stoenescu Aug 20 '14 at 10:03
  • @IonuțG.Stan Yup, it woild avoid using the classic: _.meth(_), which leads to no reusability of those functions. – Radu Stoenescu Aug 20 '14 at 10:06
  • Given you want to map a string ("::" in your example) which could represent any method, I don't see how you can avoid reflection and manual type checking (since at compile-time the compiler cannot know the type of the method). What better alternative were you hoping for? – The Archetypal Paul Aug 20 '14 at 10:32
  • I'm pretty sure this can be done with macros (a simple quasiquote might actually do it), but I'm not very well versed with them. Looking forward to Travis Brown's answer :) – Ionuț G. Stan Aug 20 '14 at 10:36
  • The more I think about it, the more it seems like overkill. What is that you don't like about underscore expansion. Your example would look like this: `List(1,2,3).foldLeft(List.empty[Int])(flip(_ :: _))` where `def flip[A,B,C](fn: (A,B) => C): (B,A) => C = (b,a) => fn(a,b)`. – Ionuț G. Stan Aug 20 '14 at 11:10
  • @IonuțG.Stan I agree that it may be an overkill if you view it just as a matter of style, but you can go further an get a sort of duck-typing: a function that takes whatever you send to it and performs a certain operation. I was thinking that `invokeDynamic` has to do with this but I haven't looked into it further. – Radu Stoenescu Aug 20 '14 at 18:46

1 Answers1

0

You can achieve it easily with plain old subtype polymorphism. Just declare interface

trait Iface[B, C] {
    def meth(b: B): C
}

Then you could implement polish easily

def polish[B, C](f: (Iface[B, C], B) => C): (Iface[B, C], B) => C = { (a, b) =>
    f(a, b)
}

Using it is completely typesafe

object IfaceImpl extends Iface[String, String] {
    override def meth(b: String): String = b.reverse
}

polish((a: Iface[String, String], b: String) => a meth b)(IfaceImpl, "hello")

Update:

Actually, you could achieve it using closures only

def polish[A, B, C](f: (A, B) => C): (A, B) => C = f

class Foo {
  def meth(b: String): String = b.reverse
}

polish((_: Foo) meth (_: String))(new Foo, "hello")

Or without helper function at all :)

val polish = identity _ // Magic at work

((_: Foo) meth (_: String))(new Foo, "hello")
lambdas
  • 3,990
  • 2
  • 29
  • 54