Abusing unsafePerformIO to catch partial functions

108 Views Asked by At

So it occurred to me today that I could perform a little trick.

{-# LANGUAGE TypeApplications #-}

import Control.Exception
import System.IO.Unsafe

catchPartial :: a -> Maybe a
catchPartial x =
    case unsafePerformIO $ try @ErrorCall (evaluate x) of
      Left _ -> Nothing
      Right x' -> Just x'

This is a strange little function that takes a (presumably unevaluated) value of type a, evaluates it to WHNF, and converts error / undefined calls to Nothing. It actually works. I can use it on Prelude partial functions like toEnum and head.

toEnum' :: Enum a => Int -> Maybe a
toEnum' n = catchPartial (toEnum n)

head' :: [a] -> Maybe a
head' xs = catchPartial (head xs)

 

*Main> toEnum' 0 :: Maybe Bool
Just False
*Main> toEnum' 1 :: Maybe Bool
Just True
*Main> toEnum' 2 :: Maybe Bool
Nothing
*Main> head' []
Nothing
*Main> head' [1, 2, 3]
Just 1

This seems to be a catch-all for many of the partial function problems of Prelude. And, at least in my primitive tests of it, it seems to work pretty well. Yet I haven't seen it used anywhere, and I see a lot of people complaining about the Prelude having partial functions. I'm always wary of unsafePerformIO, and factoring the error through IO to detect it just reeks of red flags.

So my question is: what fundamental problem am I not seeing here? Is this actually a reasonable use of unsafePerformIO? Is it prohibitively expensive to run (I haven't benchmarked it for performance)? Is there some bizarre semantic error (involving seq, perhaps) that makes it not viable for production?

0

There are 0 best solutions below