Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell / GHC: Unsafely deconstruct a constructor?

Here's a specific case of the general question I'm really asking: Suppose I'm in a performance critical section of code and I've received a value

x :: Maybe Int

I know it's a Just Int and not a Nothing, but due to code out of my control, I can't arrange to receive the actual Int directly. I want to do

case x of
    Just i -> whatever i

but I don't want GHC to insert any checking or error handling or jumps or anything; just interpret the bits as if they are of the form Just i and let me deal with the consequences.

Is this possible? (Yes, I know this is something one shouldn't do regularly.)

like image 720
Michael Benfield Avatar asked Aug 16 '15 12:08

Michael Benfield


3 Answers

Sometimes you can help GHC figure out that a GADT pattern match is total by adding some pattern signatures. I'm not really sure why that sometimes helps but it can. If you really want unsafe, the way to do it is to make a more informative GADT than the one you are working with and unsafely coerce to it. Something like this:

data Shmaybe :: Bool -> * -> * where
  Noway :: Shmaybe r a
  Shucks :: a -> Shmaybe True a

fromShucks :: ShMaybe True a -> a
fromShucks (Shucks a) = a

unsafeFromJust :: forall a . Maybe a -> a
unsafeFromJust m = fromShucks (unsafeCoerce m :: Shmaybe True a)

It's important that the constructors match in both argument types and order. Obviously, this is all absurdly unsafe and comes with no guarantees. Segmentation faults may happen.

like image 182
dfeuer Avatar answered Oct 31 '22 17:10

dfeuer


Consider using the YOLO typeclass https://gist.github.com/nkpart/8922083d3c18a8f777b8

instance Yolo Maybe where
  yolo (Just x) = x

N.B: That was a joke. Use fromJust.

like image 23
Arnon Avatar answered Oct 31 '22 17:10

Arnon


I am aware of several possibilities, but all implicitly include code for throwing errors in the Nothing case.

Irrefutable pattern:

λ> let Just a = Just "hi"
λ> a
"hi"
λ> let Just a = Nothing
λ> a
*** Exception: <interactive>:4:5-20: Irrefutable pattern failed for pattern Data.Maybe.Just a

Non-exhastive patterns:

λ> let f (Just a) = a
λ> f (Just "hi")
"hi"
λ> f Nothing
*** Exception: <interactive>:6:5-18: Non-exhaustive patterns in function f

fromJust (check and throw error):

λ> fromJust (Just "hi")
"hi"
λ> fromJust Nothing
*** Exception: Maybe.fromJust: Nothing

You cannot use unsafeCoerce because for all a the internal representations of Maybe a and a are different, and I am not aware of a way to tell GHC to not check the other cases in an irrefutable pattern.

Have you shown that this behaviour has undesirable performance characteristics and that there aren't other, easier optimisations? Unless you have, I would not worry about it :)

like image 26
frasertweedale Avatar answered Oct 31 '22 18:10

frasertweedale