Safe Haskell | None |
---|---|

Language | Haskell2010 |

- class ReallyUnsafely t => Unsafely t
- unsafely :: forall proxy t a. proxy t -> (Unsafely t => a) -> a

# Introduction

This module aims at providing simple interface to express some operations or instance are considered to be *unsafe*.

The motivation of this package is somewhat similar to
`NullaryTypeClasses`

extension,
but permits more flexible access control. See example for more detail.

# Interface

class ReallyUnsafely t => Unsafely t Source

The constraint for the instances which might be *unsafe* in some sence.
`t`

in `Unsafely t`

is the dummy tag for some series of *unsafe* operations.

impossible

ReallyUnsafely k t => Unsafely k t |

unsafely :: forall proxy t a. proxy t -> (Unsafely t => a) -> a Source

Evaluate the value which might be unsafe.

# Example: semigroup instance

The first example is about *unsafe* instances. Below, we use `EmptyDataDecls`

, `FlexibleContexts`

, `FlexibleInstances`

,
`RankNTypes`

, `TypeSynonymInstances`

and `UndecidableInstances`

extensions.

Let's think about semigroups. Semigroups are sets equipped with binary relation, which satisfies assosiative law. i.e:

a + (b + c) == (a + b) + c

We denote this binary operation as `.+.`

.

class Semigroup a where (.+.) :: a -> a -> a infixl 6 .+.

`Int`

, `Integer`

and `Rational`

s are naturally semigroups with their ordinary addition:

instance Semigroup Int where (.+.) = (+) instance Semigroup Integer where (.+.) = (+) instance Semigroup Rational where (.+.) = (+)

But, unfortunately, `Double`

is not semigroup because it doesn't satisfies associative law.
For example:

(1.9546929672907305 + 2.0) + 0.14197132377740074 == 4.096664291068132 1.9546929672907305 + (2.0 + 0.14197132377740074) == 4.096664291068131 -- different!

But, in some cases, we need `Semigroup`

instance for `Double`

for convenience.
So we provide `Semigroup Double`

instance with the *unsafety* precondition:

data ViolateAssocLaw unsafelyViolate :: (`Unsafely`

ViolateAssocLaw => a) -> a unsafelyViolate =`unsafely`

(`Proxy`

::`Proxy`

ViolateAssocLaw) instance`Unsafely`

ViolateAssocLaw => Semigroup Double where (.+.) = (+)

In above, `ViolateAssocLaw`

is the tag for unsafe `Semigroup`

instance which may violate associative law.
For the convenience, we also defined the convenient function `unsafelyViolate`

.

With the code above, we get the following:

`>>>`

3`1 .+. 2 :: Int`

`>>>`

5 % 2`2 .+. 0.5 :: Rational`

`>>>`

No instance for (Data.Constraint.Unsafely.Really.ReallyUnsafely ViolateAssocLaw) arising from a use of `.+.' Possible fix: add an instance declaration for (Data.Constraint.Unsafely.Really.ReallyUnsafely ViolateAssocLaw) In the expression: 1.0 .+. 2.0 :: Double In an equation for `it': it = 1.0 .+. 2.0 :: Double`3.0 .+. 0.4 :: Double`

`>>>`

7.0`unsafelyViolate $ 3.0 .+. 4.0 :: Double`

## Allow unsafe operations globally

Things seems to be good! But, what is the `ReallyUnsafely`

type-class?

This class prevents users from writing `Unsafely`

instace globaly and contaminating entire program.
This is because `ReallyUnsafely`

is not exported in this module and `ReallyUnsafely`

is the superclass of `Unsafely`

.

On the other hand, there is the case to permit *unsafe* operations globally. In such situation, Data.Constraint.Unsafely.Really module enables you to write such an instace:

`>>>`

<interactive>:4:3: No instance for (ReallyUnsafely * ViolateAssocLaw) arising from a use of `.+.' Possible fix: add an instance declaration for (ReallyUnsafely * ViolateAssocLaw) In the expression: 3 .+. 4 :: Double In an equation for `it': it = 3 .+. 4 :: Double`3 .+. 4 :: Double`

`>>>`

`import Data.Constraint.Unsafely.Really`

`>>>`

`instance ReallyUnsafely ViolateAssocLaw`

`>>>`

7.0`3 .+. 4 :: Double`

As above, once you declare the `ReallyUnsafely`

instance for your tag, you can use unsafe operations anywhere in your code.

# Example: restrict the access for unsafe operations

Another example is slightly safer `unsafePerformIO`

.
As above, we can provide the following interface:

module Main where import Data.Constraint.Unsafely import Data.IORef import Data.Proxy import System.IO.Unsafe saferUnsafePerformIO :: Unsafely IO => IO a -> a saferUnsafePerformIO = unsafePerformIO unsafelyIO :: (Unsafely IO => a) -> a unsafelyIO = unsafely (Proxy :: Proxy IO)

In this example, we use `IO`

type as the tag of unsafety precondition instead of defining new empty data-type.
`Unsafely`

class is kind-polymorphic, so it can take any type of any kind as its parameter.

With this, we can create the global state, which is inaccessible without `Unsafely IO`

.

global :: Unsafely IO => IORef Int global = saferUnsafePerformIO $ newIORef 0 main :: IO () main = do unsafelyIO $ readIORef global return ()