Differentiate Values with Newtype

Summary

You have multiple references, often function arguments, that share a common data type.

Make the types of each reference distinct using newtype.

Strong type, ambiguous meaning

One of my favorite advantages of static type systems is how safe they make the process of refactoring. When I change a function's type signature, the compiler lets me know, before I ever try to run my code, everywhere I need to update my program to work with the change.

Consider the following refactoring; I want setName, which currently takes arguments for first and last names, to take just a single argument for a full name.

def setName(first: String, last: String): Unit

                    ||
                    ||
                    \/

def setName(fullName: String): Unit

This change will ripple compile errors through my program, showing me each place I need to update my calls to it. Unfortunately, refactoring code does not always imply a type signature change, and API errors can sneak in to my code under the compiler's radar.

Consider the following refactoring; I want to swap the order of two arguments of the same type.

def search(haystack: String, needle: String): Unit

                    ||
                    ||
                    \/

def search(needle: String, haystack: String): Unit

This breaks any uses I have of the search function, but the compiler isn't able to let me know about it because it can't distinguish needle from haystack; they're both Strings. The search function is "stringly typed".

One way around this is to make discrete record types for needle and haystack, so that this refactoring causes a change in the type signature of search. Unfortunately, this single-value "boxing" of data leads to an ever-growing pile of new data types, each of which imposes additional CPU and memory overhead.

Newtype

What I want is a way to distinguish between these values at compile time, but avoid extra computational burden at runtime. It turns out that several languages support this idea, commonly known as "newtype".

Examples

Go

Go supports type identities via the type keyword.

search.go:

package main

import "fmt"
import "strings"

type Needle string
type Haystack string

func search(n Needle, h Haystack) bool {
  return strings.Contains(string(h), string(n))
}

func main() {

  const n Needle = "needle"
  const h Haystack = "This haystack is nothing but needles!"

  fmt.Println(search(n, h))
}
$ go run search.go
true

Haskell

Haskell supports newtype via the newtype keyword.

search.hs:

import Data.List (isSubsequenceOf)

newtype Needle = Needle String
newtype Haystack = Haystack String

search :: Needle -> Haystack -> Bool
search (Needle n) (Haystack h) = isSubsequenceOf n h

main :: IO ()
main = do
  let n = Needle "needle"
  let h = Haystack "This haystack is nothing but needles!"
  print $ search n h
$ runhaskell search.hs
True

Scala

Scala does not natively support newtype, but a basic approximation can be written in a few lines using phantom types.

Search.scala:

object Search {

  trait Needle
  trait Haystack

  def apply(n: String with Needle, h: String with Haystack): Boolean =
    h contains n
}

implicit class Tagged[A](val a: A) extends AnyVal {
  def tag[B]: A with B = a.asInstanceOf[A with B]
}

object Main extends App {

  import Search.Needle
  import Search.Haystack

  val n: String with Needle = "needle".tag[Needle]
  val h = "This haystack is nothing but needles!".tag[Haystack]

  println(Search(n, h))
}
$ scala Search.scala
true

Support for newtype in Scala is also available in libraries like Scalaz and Shapeless.

Java

Java very does not natively support newtype, but we can adapt our Scala approach to use a boxed tagger.

Search.java:

class Tagged<A, B> {
  public final A value;
  public Tagged(final A value) {
    this.value = value;
  }
  public static <A, B> Tagged<A, B> tag(final A value) {
    return new Tagged<>(value);
  }
}

public class Search {

  interface Needle { }
  interface Haystack { }

  static boolean search(
    final Tagged<String, Needle> needle,
    final Tagged<String, Haystack> haystack) {
    return haystack.value.indexOf(needle.value) != -1;
  }

  public static void main(String[] args) {

    final Tagged<String, Needle> n =
      Tagged.tag("needle");

    final Tagged<String, Haystack> h =
      Tagged.tag("This haystack is nothing but needles!");

    System.out.println(search(n, h));
  }
}
$ scala-cli Search.java
true