# Anti-patterns (WIP)

This is a collection of anti-patterns that I have observed happening out in the wild.

## Default parameters and too many binary parameters

Let’s take a look at the following snippet.

    override def interval(lowerBound: Numeric, upperBound: Numeric,
maxObserved: Numeric, minInclusiveInMiddle: Boolean = false,
useUpperBound: Option[Numeric] = None): Interval[Numeric] =
Interval(
Bound(lowerBound, inclusive = if (minInclusiveInMiddle) true else false),
if (upperBound == maxObserved)
useUpperBound.map(ub => Bound(ub, inclusive = true))
.getOrElse(Bound(maxOrderedValue, inclusive = false))
else Bound(upperBound, inclusive = true)
)

Can you describe in a single sentence what this method does? I can’t. Why? Because there are at least four different methods hidden here. Default parameters should be used only if the behavior of the function does not significantly change based on those parameters.

## Special values without new types

  val outOfRangeValue: Datum = -2
val blankDatum: Datum = -3
val ordinalNaN: Datum = -4
val alphaMarker: Datum = -5
val groupSubstitute: Datum = -6

This is highly problematic because it makes it difficult to track which values require special handling, and which do not. You end up with code like this:

if (foo == blankDatum) { ... } else {
// assume it's not blank
...
}

And all hell breaks loose when it turns out that outOfRangeValue is one of possible values for foo as well.

It is much more preferable to either define a new type,

data Datum = Normal Int | Blank | OOR

or a newtype with custom constructors and deconstructors.