> {-# LANGUAGE FlexibleContexts, Rank2Types #-} > class Unsafe a whereThe "a" parameter is unneccessary, but it is required by GHC.
Add the unsafe constraint to each operation.
> unsafePerformIO :: Unsafe () => IO a -> a > unsafePerformIO = undefined -- implementation skipped > unsafeCoerce :: Unsafe () => a -> b > unsafeCoerce = undefined
That's it. You can now code with unsafe functions. The constraint "Unsafe" is contagious: everything that internally uses unsafe* will be tagged. Old code does not have to be changed except type signatures.
The compiler only has to prohibit users from defining instance Unsafe (). If you do this, you are releasing the brakes, and back to normal Unsafe Haskell.
Safe Haskell has "trustworthy" modules, which can use unsafe features, but their authors claim their interfaces are safe.
This idea can be implemented if there was a special function allowed in trustworthy modules removing the constraint:
> unsafe :: forall a. (Unsafe () => a) -> a > unsafe = undefined
Of course this falls short of real Safe Haskell, which must forbid Template Haskell, overlapping instances etc. I like the fact that unsafeness of unsafePerformIO is expressed in its type.
I hope this demonstrates zero-parameter type classes might be something reasonable. For now, you can simulate them with -XConstraintKinds and -XDataKinds:
{-# LANGUAGE ConstraintKinds, KindSignatures, DataKinds #-}
class Unsafe' (a :: ())
type Unsafe = Unsafe' '()
Edit:
I just realized you can have unsafe instances:
instance (Unsafe (), Show a) => Show (IORef a) where show = show . unsafePerformIO . readIORef