packages feed

unsafely (empty) → 0.1.0.0

raw patch · 8 files changed

+345/−0 lines, 8 filesdep +basedep +taggeddep +unsafelysetup-changed

Dependencies added: base, tagged, unsafely

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Hiromi ISHII++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Hiromi ISHII nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,4 @@+unsafely+========++Flexible access control for unsafe operations and instances
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ examples/safe-unsafe.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}+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++global :: Unsafely IO => IORef Int+global = saferUnsafePerformIO $ newIORef 0++unsafelyIO :: (Unsafely IO => a) -> a+unsafelyIO = unsafely (Proxy :: Proxy IO)++main :: IO ()+main = do+  unsafelyIO $ readIORef global+  return ()
+ examples/semigroup.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE EmptyDataDecls, FlexibleContexts, FlexibleInstances, RankNTypes #-}+{-# LANGUAGE TypeSynonymInstances, UndecidableInstances                      #-}+module Main where+import Data.Constraint.Unsafely+import Data.Proxy++class Semigroup a where+  -- | binary operation which satisifies associative law:+  --   @(a .+. b) .+. c == a .+. (b .+. c)@+  (.+.) :: a -> a -> a++infixl 6 .+.++-- | 'Int', 'Integer' and 'Rational' satisfies associative law.+instance Semigroup Int where+  (.+.) = (+)+instance Semigroup Integer where+  (.+.) = (+)+instance Semigroup Rational where+  (.+.) = (+)++-- | Dummy type indicating the computation which may /unsafely/ violates associative law.+data ViolateAssocLaw++-- | Helper function to use /unsafe/ instance for @Semigroup@+unsafelyViolate :: (Unsafely ViolateAssocLaw => a) -> a+unsafelyViolate = unsafely (Proxy :: Proxy ViolateAssocLaw)++-- | 'Double' doesn't satsfies associative law:+--+-- > (1.9546929672907305 + 2.0) + 0.14197132377740074 == 4.096664291068132+-- > 1.9546929672907305 + (2.0 + 0.14197132377740074) == 4.096664291068131+--+-- But sometimes there is the case the instance for @Semigroup@ for Double is required.+-- So we use @Unsafely@ to mark this instance is somewhat unsafe.+instance Unsafely ViolateAssocLaw => Semigroup Double where+  (.+.) = (+)++main :: IO ()+main = do+  print (1 .+. 2 :: Int)+  unsafelyViolate $ print (3 .+. 4 :: Double)+  -- You cannot done as above, if you drop @unsafelyViolate@.+  -- Uncommenting following line causes type error.+  -- print (5 .+. 6 :: Double)++
+ src/Data/Constraint/Unsafely.hs view
@@ -0,0 +1,154 @@+{-# OPTIONS_GHC -fno-warn-unused-binds #-}+module Data.Constraint.Unsafely (+  -- * Introduction+  -- $introduction++  -- * Interface+  Unsafely, unsafely,++  -- * Example: semigroup instance+  -- $semigroup++  -- ** Allow unsafe operations globally+  -- $really++  -- * Example: restrict the access for unsafe operations+  -- $restriction+) where+import Data.Constraint.Unsafely.Really++{- $introduction #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+@<https://ghc.haskell.org/trac/ghc/ticket/7642 NullaryTypeClasses>@ extension,+but permits more flexible access control. See <#g:3 example> for more detail.+-}++{- $semigroup+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:++>>> 1 .+. 2 :: Int+3+>>> 2 .+. 0.5 :: Rational+5 % 2+>>> 3.0 .+. 0.4 :: Double+    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+>>> unsafelyViolate $ 3.0 .+. 4.0 :: Double+7.0+-}++{- $really+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:++>>> 3 .+. 4 :: Double+<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+>>> import Data.Constraint.Unsafely.Really+>>> instance ReallyUnsafely ViolateAssocLaw+>>> 3 .+. 4 :: Double+7.0++As above, once you declare the 'ReallyUnsafely' instance for your tag, you can use unsafe operations anywhere in your code.+-}++{- $restriction+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 ()+@+-}
+ src/Data/Constraint/Unsafely/Really.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE FlexibleContexts, FlexibleInstances, PolyKinds, RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables, UndecidableInstances                  #-}+-- | Really unsafe module to provide internal interface.+-- This module should be imported if you wish to allow the unsafe computation globally.+--+-- But be careful: the unsafety contaminates entire the program once you write+-- the instance for 'ReallyUnsafe' somewhere.+module Data.Constraint.Unsafely.Really (+  ReallyUnsafely, Unsafely, unsafely+  ) where+import Data.Proxy+import Unsafe.Coerce++-- | The trick type-class to prevent providing global instances for @Unsafely@.+-- This class is not exported in "Data.Constraint.Unsafely", so if you want to+-- permit unsafe operations globally, you should import this module directly.+class ReallyUnsafely t++-- | 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.+class ReallyUnsafely t => Unsafely (t :: k) where+  impossible :: Impossible t+instance ReallyUnsafely t => Unsafely t where+  impossible = Impossible+  +-- | Witness to allow /unsafe/ computation.+-- This type never be exported, which ensures that you can call @unsafely@ safely.+data Impossible (t :: k) = Impossible++newtype Thingy t a = Thingy (Unsafely t => a)++-- | Evaluate the value which might be unsafe.+unsafely :: forall t a. Proxy t -> (Unsafely t => a) -> a+unsafely _ f = unsafeCoerce (Thingy f :: Thingy t a) Impossible
+ unsafely.cabal view
@@ -0,0 +1,54 @@+name:                unsafely+version:             0.1.0.0+synopsis:            Flexible access control for unsafe operations and instances+description:         This module aims at providing simple interface to control the acccess for /unsafe/+                     operations and instance.++                     The motivation of this package is somewhat similar to+                     @<https://ghc.haskell.org/trac/ghc/ticket/7642 NullaryTypeClasses>@ extension,+                     but permits more flexible access control.++homepage:            http://github.com/konn/unsafely+license:             BSD3+license-file:        LICENSE+author:              Hiromi ISHII+maintainer:          konn.jinro_at_gmail.com+copyright:           2014 (c) Hiromi ISHII+category:            Data+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10++flag examples+  default: False++source-repository head+  type: git+  location: git://github.com/ekmett/lens.git++library+  exposed-modules:     Data.Constraint.Unsafely, Data.Constraint.Unsafely.Really+  other-extensions:    FlexibleContexts, FlexibleInstances, RankNTypes, ScopedTypeVariables, UndecidableInstances+  build-depends:       base >=4.6 && <4.7, tagged >=0.6 && <0.7+  hs-source-dirs:      src+  default-language:    Haskell2010++executable semigroup+  if flag(examples)+    buildable:           True+  else+    buildable:           False+  build-depends:       base >=4.6 && <4.7, tagged >=0.6 && <0.7, unsafely+  hs-source-dirs:      examples+  main-is:             semigroup.hs+  default-language:    Haskell2010++executable safe-unsafe+  if flag(examples)+    buildable:           True+  else+    buildable:           False+  build-depends:       base >=4.6 && <4.7, tagged >=0.6 && <0.7, unsafely+  hs-source-dirs:      examples+  main-is:             safe-unsafe.hs+  default-language:    Haskell2010