Make42 Make42 - 1 month ago 11
Scala Question

How to write a currying Scala Function trait?

Issue



First approach



If would like to have

trait Distance extends ((SpacePoint, SpacePoint) => Double)

object EuclideanDistance extends Distance {
override def apply(sp1: SpacePoint, sp2: SpacePoint): Double = ???
}

trait Kernel extends (((Distance)(SpacePoint, SpacePoint)) => Double)

object GaussianKernel extends Kernel {
override def apply(distance: Distance)(sp1: SpacePoint, sp2: SpacePoint): Double = ???
}


However the
apply
of
object GaussianKernel extends Kernel
is not an excepted
override
to the
apply
of
trait Kernel
.

Second approach - EDIT: turns out this works afterall...



Alternatively I could write

trait Kernel extends ((Distance) => ( (SpacePoint, SpacePoint) => Double))

object GaussianKernel extends Kernel {
override def apply(distance: Distance): (SpacePoint, SpacePoint) => Double =
(sp1: SpacePoint, sp2: SpacePoint) =>
math.exp(-math.pow(distance(sp1, sp2), 2) / (2))
}


but am not sure this is currying...

EDIT: Turns out that I can use this second approach in a currying fashion. I think it is exactly what the typical currying is, only without the syntactic sugar.




Explanation of the idea



The idea is this: For my algorithm I need a Kernel. This kernel calculates a metric for two vectors in space - here
SpacePoint
s. For that the Kernel requires a way to calculate the distance between the two
SpacePoint
s. Both distance and kernel should be exchangeable (open-closed principle), thus I declare them as traits (in Java I had them declared as interfaces). Here I use the Euclidean Distance (not shown) and the Gaussian Kernel. Why the currying? Later when using those things, the
distance
is going to be more or less the same for all measurements, while the
SpacePoint
s will change all the time. Again, trying to stay true to the open-closed principle. Thus, in a first step I would like the
GaussianKernel
to be pre-configured (if you will) with a distance and return a
Function
that can be feed later in the program with the
SpacePoint
s (I am sure the code is wrong, just to give you an idea what I am aiming at):

val myFirstKernel = GaussianKernel(EuclideanDistance)
val mySecondKernel = GaussianKernel(FancyDistance)
val myThirdKernel = EpanechnikovKernel(EuclideanDistance)
// ... lots lof code ...
val firstOtherClass = new OtherClass(myFirstKernel)
val secondOtherClass = new OtherClass(mySecondKernel)
val thirdOtherClass = new OtherClass(myThirdKernel)

// ... meanwhile in "OtherClass" ...
class OtherClass(kernel: Kernel) {
val thisSpacePoint = ??? // ... fancy stuff going on ...
val thisSpacePoint = ??? // ... fancy stuff going on ...
val calculatedKernel = kernel(thisSpacePoint, thatSpacePoint)
}





Questions




  1. How do I build my trait?

  2. Since
    distance
    can be different for different
    GaussianKernel
    s - should
    GaussianKernel
    be a class instead of an object?

  3. Should I partially apply
    GaussianKernel
    instead of currying?

  4. Is my approach bad and
    GaussianKernel
    should be a class that stores the
    distance
    in a field?


Answer

Answers

1. How do I build my trait?

The second approach is the way to go. You just can't use the syntactic sugar of currying as usual, but this is the same as currying:

GaussianKernel(ContinuousEuclideanDistance)(2, sp1, sp2)
GaussianKernel(ContinuousManhattanDistance)(2, sp1, sp2)

val eKern = GaussianKernel(ContinuousEuclideanDistance)

eKern(2, sp1, sp2)
eKern(2, sp1, sp3)

val mKern = GaussianKernel(ContinuousManhattanDistance)

mKern(2, sp1, sp2)
mKern(2, sp1, sp3)

Why the first approach does not work

Because currying is only possible for methods (duh...). The issue starts with the notion that a Function is very much like a method, only that the actual method is the apply method, which is invoked by calling the Function's "constructor".

First of all: If an object has an apply method, it already has this ability - no need to extend a Function. Extending a Function only forces the object to have an apply method. When I say "object" here I mean both, a singleton Scala object (with the identifier object) and a instantiated class. If the object is a instantiated class MyClass, then the call MyClass(...) refers to the constructor (thus a new before that is required) and the apply is masked. However, after the instantiation, I can use the resulting object in the way mentioned: val myClass = new MyClass(...), where myClass is an object (a class instance). Now I can write myClass(...), calling the apply method. If the object is a singleton object, then I already have an object and can directly write MyObject(...) to call the apply method. Of course an object (in both senses) does not have a constructor and thus the apply is not masked and can be used. When this is done, it just looks the same way as a constructor, but it isn't (that's Scala syntax for you - just because it looks similar, doesn't mean it's the same thing).

Second of all: Currying is syntactic sugar:

def mymethod(a: Int)(b: Double): String = ???

is syntactic sugar for

def mymethod(a: Int): ((Double) => String) = ???

which is syntactic sugar for

def mymethod(a: Int): Function1[Double, String] = ???

thus

def mymethod(a: Int): Function1[Double, String] = {
    new Function1[Double, String] {
        def apply(Double): String = ???
    }
}

(If we extend a FunctionN[T1, T2, ..., Tn+1] it works like this: The last type Tn+1 is the output type of the apply method, the first N types are the input types.)

Now, we want the apply method here is supposed to be currying:

object GaussianKernel extends Kernel {
  override def apply(distance: Distance)(sp1: SpacePoint, sp2: SpacePoint): Double = ???
}

which translates to

object GaussianKernel extends Kernel {
    def apply(distance: Distance): Function2[SpacePoint, SpacePoint, Double] = {
        new Function2[SpacePoint, SpacePoint, Double] {
            def apply(SpacePoint, SpacePoint): Double
        }
    }
}

Now, so what should GaussianKernel extend (or what is GaussianKernel)? It should extend

Function1[Distance, Function2[SpacePoint, SpacePoint, Double]]

(which is the same as Distance => ((SpacePoint, SpacePoint) => Double)), the second approach).

Now the issue here is, that this cannot be written as currying, because it is a type description and not a method's signature. After discussing all this, this seems obvious, but before discussion all this, it might not have. The thing is, that the type description seemed to have a direct translation into the apply method's (the first, or only one, depending on how one takes the syntactic sugar apart) signature, but it doesn't. To be fair though, it is something that could have been implemented in the compiler: That the type description and the apply method's signature are recognized to be equal.

2. Since distance can be different for different GaussianKernels - should GaussianKernel be a class instead of an object?

Both are valid implementation. Using those later only differenciates only in the presence or absence of new.

If one does not like the new one can consider a companion object as a Factory pattern.

3. Should I partially apply GaussianKernel instead of currying?

In general this is preferred according to http://www.vasinov.com/blog/on-currying-and-partial-function-application/#toc-use-cases

An advantage of currying would be the nicer code without _: ??? for the missing parameters.

4. Is my approach bad and GaussianKernel should be a class that stores the distance in a field?

see 2.