From F# to Scala — apply & unapply functions

Read the whole series:

Part 1 — type infer­ence

Part 2 — traits

Part 3 — case class/object (ADTs)

Part 4 — apply & unap­ply func­tions <- you’re here

Part 5 — implic­its


Last time around we looked at Scala’s Case Class in depth and how it com­pares to F#‘s Dis­crim­i­nat­ed Unions. F# also has Active Pat­terns, which is a very pow­er­ful lan­guage fea­ture in its own right. Unsur­pris­ing­ly, Scala also has some­thing sim­i­lar in the shape of extrac­tors (via the unap­ply func­tion).

Before we can talk about extrac­tors we have to first talk about Scala’s object again. Remem­ber when we first met object in Scala I said it’s Scala’s equiv­a­lent to F#‘s mod­ule? (except it can be gener­ic, sup­ports inher­i­tance, and mul­ti­ple inher­i­tance)

Well, turns out Scala has anoth­er bit of spe­cial mag­ic in the form of an apply func­tion.

 

The apply function

In Scala, if you assign a func­tion to a val­ue, that val­ue will have the type Function1[TInput, TOut­put]. Since every­thing in Scala is an object, this val­ue also have a cou­ple of func­tions on it.

You can use andThen or com­pose to com­pose it with anoth­er func­tion (think of them as F#‘s » and « oper­a­tors respec­tive­ly).

The apply func­tion applies the argu­ment to the func­tion, but you can invoke the func­tion with­out it.

Ok, now that we know what apply function’s role is, let’s go back to object.

If you declare an apply func­tion in an object, it essen­tial­ly allows the object to be used as a fac­to­ry class (indeed this is called the Fac­to­ry pat­tern in Scala).

You see this pat­tern in Scala very often, and there are some use­ful built-in fac­to­ries such as Option (which wraps an object as Some(x) unless it’s null, in which case returns None).


String and Big­Int defines their own apply func­tion too (in String’s case, it returns the char at the spec­i­fied index) .

You can also define an apply func­tion on a class as well as an object, and it works the same way. For instance…

I find this notion of apply­ing argu­ments to an object some­what alien, almost as if this is an elab­o­rate way of cre­at­ing a del­e­gate even though Scala already have first-class func­tions…

Ok, can you pass mul­ti­ple argu­ments to apply? What about over­load­ing?

Check, and Check.

What about case class­es and case object?

Check, and Check.

Ok. Can the apply function(s) be inher­it­ed and over­rid­den like a nor­mal func­tion?

Check, and Check. Although this is con­sis­tent with inher­i­tance and OOP in Java, I can’t help but to feel it has the poten­tial to cre­ate ambi­gu­i­ty and one should just stick with plain old func­tions.

 

The unapply function (aka extractors)

When you cre­ate a case class or case object, you also cre­ate a pat­tern that can be used in pat­tern match­ing. It’s not the only way to cre­ate pat­terns in Scala, you can also cre­ate a pat­tern by defin­ing an unap­ply func­tion in your class/object.

or, if you don’t want to return any­thing from the pat­tern.

So, the unap­ply func­tion turns a Scala object into a pat­tern, and here are some lim­i­ta­tions on the unap­ply func­tion:

  1. it can only take one argu­ment
  2. if you want to return a val­ue, then the return type T must defines mem­bers:
    • isEmp­ty: Boolean
    • get: Any

side note: point 2 is inter­est­ing. Look­ing at all the exam­ples on the Inter­net one might assume the unap­ply func­tion must return an Option[T], but turns out it’s OK to return any type so long it has the nec­es­sary mem­bers!

Whilst I can’t think of a sit­u­a­tion where I’d need to use any­thing oth­er than an Option[T], this insight gives me a bet­ter under­stand­ing of how pat­tern match­ing in Scala works.

Whether or not the pat­tern match­es is deter­mined by the val­ue of isEmp­ty of the result type T. And the val­ue returned by your pat­tern — ie msg in the exam­ple above — is deter­mined by the val­ue of get of the result type T. So if you’re feel­ing a bit cheeky, you can always do some­thing like this:


Since the unap­ply func­tion is a mem­ber on an object (like the apply func­tion), it means it should work with a class too, and indeed it does.

As you can see from the snip­pet above, this allows you to cre­ate para­me­ter­ized pat­terns and work around the lim­i­ta­tion of hav­ing only one argu­ment in the unap­ply func­tion.

You can nest pat­terns togeth­er too, for exam­ple.

Here, the Int pat­tern returns an Int, and instead of bind­ing it to a name we can apply anoth­er pat­tern inline to check if the val­ue is even.

And whilst it doesn’t get men­tioned in any of the arti­cles I have seen, these pat­terns are not lim­it­ed to the match clause either. For instance, you can use it as part of dec­la­ra­tion. (but be care­ful, as you’ll get a MatchEr­ror if the pat­tern doesn’t match!)

 

Primer: F# Active Patterns

Before we com­pare Scala’s extrac­tors to F#‘s active pat­terns, here’s a quick primer if you need to catch up on how F#‘s active pat­terns work.

Like extrac­tors, it gives you named pat­terns that you can use in pat­tern match­ing, and comes in 3 flavours: sin­gle-case, par­tial, and mul­ti-case.

You can para­me­terise a pat­tern.

If a pattern’s dec­la­ra­tion has mul­ti­ple argu­ments, then the last argu­ment is the thing that is being pat­tern matched (same as the sin­gle argu­ment to unap­ply); the pre­ced­ing argu­ments can be passed into the pat­tern at the call site. For exam­ple…

If you don’t want to return any­thing, then you can always return () or Some() instead (par­tial pat­terns require the lat­ter).

You can also mix and match dif­fer­ent pat­terns togeth­er using & and |. So we can rewrite the fizzbuzz func­tion as the fol­low­ing..

Pat­terns can be nest­ed.

Final­ly, pat­terns can be used in assign­ment as well as func­tion argu­ments too.

 

extractors vs F# Active Patterns

Scala’s extrac­tor is the equiv­a­lent of F#‘s par­tial pat­tern, and although there is no like-for-like replace­ment for sin­gle-case and mul­ti-case pat­terns you can mim­ic both with extractor(s):

  • an extrac­tor that always return Some(x) is like a sin­gle-case pat­tern
  • mul­ti­ple extrac­tors work­ing togeth­er (maybe even loose­ly grouped togeth­er via a com­mon trait) can mim­ic a mul­ti-case pat­tern, although it’s up to you to ensure the extrac­tors don’t over­lap on input val­ues

Whilst it’s pos­si­ble to cre­ate para­me­ter­ized pat­terns with Scala extrac­tors (by using class instead of object), I find the process of doing so in F# to be much more con­cise. In gen­er­al, the syn­tax for declar­ing pat­terns in Scala is a lot more ver­bose by com­par­i­son.

The biggest dif­fer­ence for me though, is that in F# you can use mul­ti­ple pat­terns in one case expres­sion by com­pos­ing them with & and |. This makes even com­plex pat­terns easy to express and under­stand.

 

Links