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
= Deprecate myFunction
myFunction_DEPRECATED where
myFunction :: A -> B
A = B myFunction
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
(💩💩)= Deprecate (d 💩 a) (💩💩) d a
Now we can deprecate a function with multiple parameters:
myFunction2_DEPRECATED :: Deprecated A (B -> C)
= Deprecate myFunction2
myFunction2_DEPRECATED where
myFunction2 :: A -> B -> C
A B = C myFunction2
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.