During a discussion at work we stumbled into the topic of best/worst features of Scala, and it was suggested that the implicit system was the worst feature.

I disagree with this, it might be misunderstood and misused, but far from worst feature (that has to be the type interference [sic!]). So in order to continue the tradition of misusing it, I'll present a view of implicits that some might not have seen or thought of.

The idea is to elucidate the Curry–Howard correspondence that's present in Scala's type system, and use implicits to automatically construct proofs. That this is possible should not be surprising, but what's interesting is how easy it is.

My motivation for this exercise is that at work we deal a lot more with
business *logic* than with computations. I hear statements such as:
"a user has payed for content, therefore he is entitled to use the content"
rather than "these are the first million prime numbers", and "P implies Q" rather
than numerical properties.

# An explicit Curry-Howard correspondence

The Curry-Howard correspondence is the observation that type-systems can be thought of as logics (and vice versa). This sounds far harder than it actually is:

- Propositions correnpond to types
- Implications correspond to functions
- Conjunctions and disjunctions both correspond to simple ADTs (or products and coproducts for the fancy)
- Negations are functions together with a bottom type

Here are some examples motivating this correspondence using Scala and its implicit system.

## Propositions

A proposition `A`

is expressed as a type:

`trait A`

A proof of proposition `A`

is expressed as an inhabitant of the type `A`

:

`implicit val proofOfA = new A {}`

So we can ask the compiler if it can prove `A`

using:

`implicitly[A]`

which would fail with a type-error for unproven propositions:

```
trait B
illTyped { "implicitly[B]" }
```

where I've used `illTyped`

from shapeless

## Non-termination

When thinking about computers and proofs, non-termination has to be taken into account, and we can't trust the type system alone.

Imagine that a mathematician says: "I have a proof of A, I just have to write it down!" and then never comes back. Do you trust him? The type system would! Consider this definition:

```
@tailrec
def diverge[A](): A = diverge[A]()
```

which makes the expression `diverge()`

an inhabitant of any type:

```
:t diverge(): Int
:t diverge(): Nothing
```

But as sceptical humans we want to see the actual proof, and so does Scala
at runtime. So when we use `implicitly`

to ask for a proof, it's crucial to wait
for the evaluation to succeed.

### Tangent: Exceptions as non-termination

Of course there's a simpler way of inhabiting `Nothing`

in Scala, and that's to
`throw`

:

`(throw new Exception): Nothing`

but the point of using `diverge`

is that even without resorting to exceptions
it's possible (and easy) to inhabit any type including the bottom type `Nothing`

.

### Tangent: Dependently typed systems

Note that in stronger type systems
more properties can be proven at compile time. For instance Idris
deals with the above example by nothing that `diverge`

is not total, and therefore rejects proofs that
uses it with the trade-off that you can trust proofs using only total functions.

## Implications

An implication is expressed as a function:

`implicit def `A implies B`(implicit a: A): B = new B {}`

Then if we have a proof of `A`

in-scope, we can prove `B`

:

```
implicit val proofOfA = new A {}
implicitly[B]
```

and of-course we can't use `C => D`

to prove `D`

without a proof of `C`

:

```
trait C
trait D
implicit def `C implies D`(implicit c: C): D = new D {}
illTyped { """implicitly[C]""" }
illTyped { """implicitly[D]""" }
```

## Conjunction

Conjunction (or `and`

) can be expressed using a tuple:

`implicit def `P, Q implies (P and Q)`[P, Q](implicit p: P, q: Q): (P, Q) = (p, q)`

We can now query if the compiler can prove or conjunction:

```
implicit val proofOfA = new A {}
implicit val proofOfB = new B {}
implicitly[(A, B)]
```

and of course when one of the proofs are missing the conjunction can't be proven:

```
illTyped { """implicitly[(A, C)]""" }
illTyped { """implicitly[(C, B)]""" }
```

## Disjunction

Disjunction (or `or`

) can be expressed using a simple ADT:

```
sealed trait Disjunction[T, S]
case class FromT[T, S](t: T) extends Disjunction[T, S]
case class FromS[T, S](s: S) extends Disjunction[T, S]
case class FromTS[T, S](t: T, s: S) extends Disjunction[T, S]
```

accompanied by some implicits to hook it up:

```
trait LowPriorityDisjunctionProofs {
implicit def disjunctionT[T, S](implicit t: T): Disjunction[T, S] = FromT(t)
implicit def disjunctionS[T, S](implicit s: S): Disjunction[T, S] = FromS(s)
}
object Disjunction extends LowPriorityDisjunctionProofs {
implicit def disjunctionTS[T, S](implicit t: T, s: S): Disjunction[T, S] = FromTS(t, s)
}
```

Let's see it in action:

```
implicit val proofOfA = new A {}
implicit val proofOfB = new B {}
illTyped { "implicitly[C]" }
illTyped { "implicitly[D]" }
implicitly[Disjunction[A, C]]
implicitly[Disjunction[C, B]]
implicitly[Disjunction[A, B]]
illTyped { "implicitly[Disjunction[C, D]]" }
```

### Why not just use `Either`

?

Good question, let's see what happens:

```
implicit def disjunctionLeft[T, S](implicit t: T): Either[T, S] = Left(t)
implicit def disjunctionRight[T, S](implicit s: S): Either[T, S] = Right(s)
```

This seems to work fine at first

```
implicitly[Either[A, C]]
implicitly[Either[C, B]]
```

but this breaks:

`illTyped { "implicitly[Either[A, B]]" }`

with the clue:

```
<console>:32: error: ambiguous implicit values:
both method disjunctionLeft of type [T, S](implicit t: T)Either[T,S]
and method disjunctionRight of type [T, S](implicit s: S)Either[T,S]
match expected type Either[A,B]
implicitly[Either[A, B]]
^
```

That is, when Scala finds two ways (with the same priority) of proving an implicit Scala will not automatically choose for you.

This is the reason for `FromTS`

above, and
the trick with putting the implicit in its companion object and
stacking implicits with lower priorities in traits.
I first encountered this idea in Scalaz
and shapeless, both libraries
are excellent examples of libraries that uses Scala's features to the fullest
and are big inspirational sources.

## Negation

To express the negation of a proposition `A`

one can use the bottom type
`Nothing`

to represent the false formula, and define:

`implicit val notA: A => Nothing = { _ => throw new Exception }`

This encoding relies on observing that `A`

and `¬A`

is a contraction:

`implicit def `A and ¬A is a contradiction`(implicit a: A, notA: A => Nothing): Nothing = notA(a)`

When we get a `Nothing`

into the implicit system the world becomes quite absurd:

```
implicit val proofOfA = new A {}
:t implicitly[B]
:t implicitly[Int]
:t implicitly[Nothing]
```

thankfully the runtime system will not be so lenient and none of

```
implicitly[B]
implicitly[Int]
implicitly[Nothing]
```

will terminate (here it'll throw the exception we `new`

:ed above).
Note that these typeable expressions will correctly be rejected with a fresh REPL:

```
scala> :t implicitly[Nothing]
<console>:21: error: could not find implicit value for parameter e: Nothing
implicitly[Nothing]
```

But it is quite fun to sneak really strange expressions past the compiler:

```
implicit def `Embrace the nothingness`[P, Q](implicit p: P): Q = implicitly[Nothing]
val b: B = new A {}
val i: Int = b
```

that of course fail at runtime.
Here's the full code
(using diverge instead) for checking that `scalac`

accepts this absurd world.

# Conclusion

Scala's implicit system might be misused and disliked by many. But I think it's one of the more interesting features of Scala, and my preferred view of it is that it's a readily available proof engine embedded into the language.

A down to earth usage that is encountered frequently in the wild are typeclasses
for JSON-codecs.
Since typeclasses are implemented in Scala as implicits, when handed the
evidence that a type `A`

has a `ToJSON`

typeclass instance by `implicitly`

, that
evince can be a constructive proof, a cookbook, of a way to convert a value of type
`A`

to JSON.

Examples of this can be seen for instance in Argonaut and it's cats based fork circe, and in spray-json with its obligatory companion spray-json-shapeless. (I can't but suspect they are inspired by the excellent aeson library, but I might be biased towards Haskell in general.)