Someone recently suggested a way to make the barrier to using deprecated code even higher. He was kidding, but I looked into how to make it work. Here is the result.

The conversation that sparked this post might have gone a little bit as follows:

A: Haskell supports unicode identifiers, right? B: Yeah, and? A: ... and unicode has emoji, right? B: Yes, ..., where are you going with this? A: Well there is this poop emoji: "💩", right? B: Uh-oh ... A: LITERAL SHITTY CODE B: *facepalm*

Deprecation in the type signature

The fact that a function is deprecated should not just show up in the function name ( _DEPRECATED suffix), or in the compilation logs as a warning. If that is all we add to discourage the use of deprecated functions, the barrier to using them may still be too low. We should actively make it un-fun to use deprecated functions.

The trick will be to have deprecation show up in type signatures. Let's get started with this annoying newtype wrapper around a function:

newtype Deprecated a b = Deprecate { useDeprecated :: a -> b }

Now, to deprecate a function, we just change a function myFunction into myFunction_DEPRECATED by putting it in a where clause and adding the _DEPRECATED suffix. (Don't forget to add a deprecation plan in the comments!)

data A = A -- For didactic purposes data B = B data C = C myFunction_DEPRECATED :: Deprecated A B myFunction_DEPRECATED = Deprecate myFunction where myFunction :: A -> B myFunction A = B

This can be done with a simple tool.

The smiling poop operators

Before we delve into how to fix up the rest of the code so that it compiles again, let us make using deprecated code even nastier. We can make a new operator to un-deprecate a Deprecated function:

(💩) :: Deprecated a b -> (a -> b) (💩) = useDeprecated

Now we can hide the useDeprecated function and the Deprecate constructor so that this operator has to be used. We can then still use myFunction_DEPRECATED if it is absolutely necessary, but it is already not very fun anymore:

λ> myFunction_DEPRECATED 💩 A B

Sadly, deprecating functions with multiple parameters will now require a lot more parentheses. That could be smellier! Here is the double poop operator:

(💩💩) :: Deprecated a (b -> c) -> a -> Deprecated b c (💩💩) d a = Deprecate (d 💩 a)

Now we can deprecate a function with multiple parameters:

myFunction2_DEPRECATED :: Deprecated A (B -> C) myFunction2_DEPRECATED = Deprecate myFunction2 where myFunction2 :: A -> B -> C myFunction2 A B = C

Usage would require both the double poop operator and the single poop operator:

λ> myFunction2_DEPRECATED 💩💩 A 💩 B C

Now this is truly shitty code!

All that's left now is to write a tool that can automatically deprecate a function and convert its usages to shitty code. I will leave that as an exercise to the reader.

DISCLAIMER: This post is at least partly satirical.