Pattern Matching Code Smell

117 views
Skip to first unread message

typele...@gmail.com

unread,
May 19, 2015, 10:29:30 PM5/19/15
to sca...@googlegroups.com
Hello,

I'm a Scala functional programming newbie and I need some advice 
from seasoned practitioners. I'm sorry if this is not correct forum but 
since I am programming in Scala and trying to be as pure as possible
I am guessing I can get some good advice here about how to code
purely in Scala.

The issue I have is in design of the code below:

sealed trait Foo
case object A extends Foo
case object B extends Foo
case object C extends Foo
// and so on

object Foo {

    private def someFunctionSemanticallyRelatedToA()
    private def someFunctionSemanticallyRelatedToB()
    private def someFunctionSemanticallyRelatedToC()
    // and so on

    def somePublicFunction(x : Foo)  = x match {
        case A => someFunctionSemanticallyRelatedToA()
        case B => someFunctionSemanticallyRelatedToB()
        case C => someFunctionSemanticallyRelatedToC()
        // and so on
    }
}
 
My questions are:
  1. Is the somePublicFunction suffering from code smell or even the whole design?
  2. Is there a better abstraction to handle this type of design?

Regards,
Iftikhar

Jed Wesley-Smith

unread,
May 19, 2015, 11:02:40 PM5/19/15
to scalaz
hi Iftikhar,

this group is more specifically related to question around the use and development of the scalaz library – but, this also aligns with functional programming in Scala more generally, so you may get some useful opinions here.

however, there is an even better forum for questions like this, https://groups.google.com/forum/#!forum/scala-functional

that forum recommends – and I enthusiastically second the recommendation – to read the fantastic book "Functional Programming in Scala" http://www.manning.com/bjarnason/ which many I know who've read it regard to be one of the finest computing tomes they've ever read.

as to your specific question, I don't think you've got a smell, you simply have chosen one axis of the expression problem, where you have a fixed set of cases (also known as an algebraic data-type) with infinitely extensible behaviour (you can't extends the dataype without breaking clients). If you allow infinite extension of the data-type then you fix the set of allowable behaviours to what is defined in the parent interface (you can't extend without breaking clients).

in OO land you'd need to encode this behaviour in the Visitor pattern (which is pretty horrible). In functional land you have a couple of other options though. For instance, one of the problems with pattern matching is that you cannot guarantee that you you cover all the cases, or that you do not have cases that obscure later cases. For instance:

def bar(x : Foo)  = x match {
  case A => …
  case _ => …
  case B => …
  case C => …
}

the specific cases for B and C will never trigger because the _ case triggers first. The compiler may be able to help for simple cases like this, but cannot when conditional case logic gets introduced.

An alternate strategy is to use a fold (aka a catamorphism) where you explicitly supply functions for each case, eg:

sealed trait Foo {
  def fold[T](a: => T, b: => T, c: => T): T  = this match {
    case A => a
    case B => b
    case C => c
  }
}

Here, each of these are lazily evaluated (by-name) expressions (the a: => T syntax) and the specific case match causes the right one to be evaluated. In your case you'd call this by:

def somePublicFunction(x : Foo) = 
  x.fold(
    someFunctionSemanticallyRelatedToA(),
    someFunctionSemanticallyRelatedToB(),
    someFunctionSemanticallyRelatedToC()
  )

This makes it impossible by design to accidentally forget to handle one of the cases.

cheers,
jed.

--
You received this message because you are subscribed to the Google Groups "scalaz" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scalaz+un...@googlegroups.com.
To post to this group, send email to sca...@googlegroups.com.
Visit this group at http://groups.google.com/group/scalaz.
For more options, visit https://groups.google.com/d/optout.

typele...@gmail.com

unread,
May 20, 2015, 5:29:55 AM5/20/15
to sca...@googlegroups.com, j...@wesleysmith.io
Hi Jed,

Thanks for the reply and recommendations.

I am actually half-way through that book. I thought I'd stop
and apply what I have learnt to a problem domain I have,
which is how I ended up with the code I posted. I will take 
a look at the fold strategy.

Regards,
Iftikhar
Reply all
Reply to author
Forward
0 new messages