Missing OO and FP bridge in Scala

Scala blends functional and object-oriented programming in many nice ways. You can use both FP an OO-like constructs whichever fits the current problem better. But there’s always room for improvement! Here’s one thing I think is missing (short version at the bottom).

FP side

It’s easy to convert a method to a function in Scala. For example, if you have a method:

1
def say(to: Person, what: String): String = { ... }

we can get the corresponding function using the underscore notation:

1
val sayFun: (Person, String) => String = say _

Moreover, Scala supports multiple parameter lists, which is sometimes also referred to as currying, and which makes partial application easier:

1
2
3
4
5
6
// Method with multiple parameter lists
def connect(person1: Person)(person2: person): Connection = { ... }
 
// Function, created by partially applying the previous method
val createConnectionWithAdam: Person => Connection = 
      connect(new Person("Adam")) _

OO side

One thing every class has is a constructor. But what is a constructor, really? You give values for the constructor’s arguments, and get a new instance of the class in return. So it’s really just a function!

1
2
3
4
class Person(name: String, age: Int) { ... }
 
// The "signature" of new is (String, Int) => Person
val somebody = new Person("John", 34)

Link?

However, that’s where the combination of OO and FP fails in Scala: you can’t use the two features of methods (convert to function, currying) mentioned above with constructors. Both of these won’t work:

1
2
3
4
val makePerson: (String, Int) => Person = new Person _
 
class Person2(name: String)(age: Int) { ... }
val makeNewJack: Int => Person = new Person2("Jack") _

You can get around this using companion objects and apply (or any other factory method, apply just has a nicer notation afterwards):

1
2
3
4
5
object Person2 {
   def apply(name: String)(age: Int) = new Person2(name, age)
}
 
val makeNewJack: Int => Person = Person2("Jack") _

But that requires repeating the signature of the constructor in the companion object, and nobody likes code duplication, right? ;)

Use-case

Where can this be useful? For example in the classic factory example. Imagine you have a class which depends on some services, but also on some data available on runtime. Of course we use IoC so instances of the other services are provided to our class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// This service depends on a concrete Person instance
class Service3(service1: Service1, service2: Service2)(person: Person) { 
   ... 
}
 
// Note that the multiple parameter notation above als provides a nice 
// separation of the parameters that should be "injected" - services, 
// and the data that can be variable.
 
// This service depends on Service1, and wants to create it having Person 
// instances
class Service4(makeService3: Person => Service3) {
  // Usage:
  for (person <- persons) makeService3(person).doSomething()
}
 
class Main {
   // Bootstrap: (or - no-framework DI container ;) )
   val service1 = new Service1
   val service2 = new Service2
   // That's the part that is illegal in Scala
   val makeService3 = new Service3(service1, service2) _
   val service4 = new Service4(makeService1)
 
   ...
 
   // Today we'd have to write: val makeService3 = 
   //     (person: Person) => new Service3(service1, service2, person)
}

That’s also connected to my post on DI and OO, and how current DI frameworks make it hard to define services which depend on data and services that we’d like to have multiple copies of.

Side note

When viewing constructors as methods/functions (which they are, really ;) ), I suppose the Ruby-like notation:

1
Person.new("John", 34)

would be better and adding support for _ and multiple parameter lists would be obvious.

Bottom line for TL;DR fans

Why not treat class constructors as every other method/function? Make this legal:

1
2
3
4
5
class Person(name: String, age: Int)
val makePerson = new Person _ // type: (String, Int) => Person 
 
class Person2(name: String)(age: Int)
val makeNewJack = new Person2("Jack") _ // type: Int => Person

Adam

  • http://nurkiewicz.blogspot.com Tomek N.

    It kind-of works for case classes (also thanks to apply): http://stackoverflow.com/questions/7695515/using-constructor-where-function-expected

  • http://chuusai.com/blog Miles Sabin

    I pretty much agree with you. Nevertheless you can get extremely close to the desired syntax mentioned in the TL;DR,

    scala> class Person(name: String, age: Int)
    defined class Person

    scala> val makePerson = new Person(_, _)
    makePerson: (String, Int) => Person =

    scala> class Person2(name: String)(age: Int)
    defined class Person2

    scala> val makeNewJack = new Person2(“Jack”)(_)
    makeNewJack: Int => Person2 =

    Obviously this is semantically quite different: here we’re using fresh anonymous function literals, whereas what you’re after is an improved form of eta-expansion for constructors.

  • http://www.underscoreconsulting.com/ Channing Walton
  • http://www.warski.org Adam Warski

    Thx, I did my master’s in category theory, but I’m sure the link will be useful :)

    Adam

  • http://www.underscoreconsulting.com/ Channing Walton

    :) I it was more a comment for your readers, I needed to look it up so thought it would help.

  • http://www.warski.org Adam Warski

    Yep, but you know, it’s good to mention cat th in every scala post, even if it’s only in the comments and unrelated ;)

  • http://www.underscoreconsulting.com/ Channing Walton

    Absolutey, bring it on!

  • Pingback: JavaPins