bluefin-internal 0.3.5.0 → 0.4.0.0
raw patch · 6 files changed
+156/−82 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Bluefin.Internal: handleMapHandle :: (forall (e :: Effects) (es :: Effects). e :> es => h e -> h es) -> HandleD h
- Bluefin.Internal: handleOneWayCoercion :: (forall (e :: Effects) (es :: Effects). e :> es => OneWayCoercion (h e) (h es)) -> HandleD h
- Bluefin.Internal: instance Bluefin.Internal.Handle h => Bluefin.Internal.Handle (Bluefin.Internal.HandleReader h)
+ Bluefin.Internal: [MkHandleDict] :: forall (h :: Effects -> Type). (forall (e :: Effects) (es :: Effects). e :> es => OneWayCoercible (h e) (h es)) => HandleDict h
+ Bluefin.Internal: data HandleDict (h :: Effects -> Type)
+ Bluefin.Internal: handleDictImpl :: forall (h :: Effects -> Type). Handle h => HandleDict h
+ Bluefin.Internal: handleDictOfHandleD :: forall (h :: Effects -> Type). HandleD h -> HandleDict h
+ Bluefin.Internal: instance (e Bluefin.Internal.:> es) => Bluefin.Internal.OneWayCoercible.OneWayCoercible (Bluefin.Internal.HandleReader h e) (Bluefin.Internal.HandleReader h es)
+ Bluefin.Internal: instance Bluefin.Internal.Handle (Bluefin.Internal.HandleReader h)
+ Bluefin.Internal: withHandle :: Handle h => ((forall (e :: Effects) (es :: Effects). e :> es => OneWayCoercible (h e) (h es)) => r) -> r
+ Bluefin.Internal.CloneableHandle: instance (Bluefin.Internal.Handle h, e Bluefin.Internal.:> es) => Bluefin.Internal.OneWayCoercible.OneWayCoercible (Bluefin.Internal.CloneableHandle.GenericCloneableHandle h e) (Bluefin.Internal.CloneableHandle.GenericCloneableHandle h es)
+ Bluefin.Internal.Examples: instance (e Bluefin.Internal.:> es) => Bluefin.Internal.OneWayCoercible.OneWayCoercible (Bluefin.Internal.Examples.Counter7 e) (Bluefin.Internal.Examples.Counter7 es)
+ Bluefin.Internal.Examples: instance (e Bluefin.Internal.:> es) => Bluefin.Internal.OneWayCoercible.OneWayCoercible (Bluefin.Internal.Examples.DynamicReader r e) (Bluefin.Internal.Examples.DynamicReader r es)
+ Bluefin.Internal.System.IO: instance (e Bluefin.Internal.:> es) => Bluefin.Internal.OneWayCoercible.OneWayCoercible (Bluefin.Internal.System.IO.Handle e) (Bluefin.Internal.System.IO.Handle es)
- Bluefin.Internal: MkHandleD :: (forall (e :: Effects) (es :: Effects). e :> es => h e -> h es) -> HandleD (h :: Effects -> Type)
+ Bluefin.Internal: MkHandleD :: HandleDict (Any :: Effects -> Type) -> HandleD (h :: Effects -> Type)
- Bluefin.Internal: mapHandle :: forall (e :: Effects) (es :: Effects). (Handle h, e :> es) => h e -> h es
+ Bluefin.Internal: mapHandle :: forall h (e :: Effects) (es :: Effects). (Handle h, e :> es) => h e -> h es
Files
- CHANGELOG.md +10/−0
- bluefin-internal.cabal +1/−1
- src/Bluefin/Internal.hs +115/−68
- src/Bluefin/Internal/CloneableHandle.hs +6/−2
- src/Bluefin/Internal/Examples.hs +15/−8
- src/Bluefin/Internal/System/IO.hs +9/−3
CHANGELOG.md view
@@ -1,3 +1,13 @@+# 0.4.0.0++* Move `mapHandle` out of class `Handle`++* Remove `Bluefin.Internal.handleOneWayCoercion`++* Remove `Bluefin.Internal.handleMapHandle`++* Add a variety of `Handle` functionality+ # 0.3.5.0 * Add `Bluefin.Internal.oneWayCoercibleTrustMe`
bluefin-internal.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: bluefin-internal-version: 0.3.5.0+version: 0.4.0.0 license: MIT license-file: LICENSE author: Tom Ellis
src/Bluefin/Internal.hs view
@@ -16,10 +16,8 @@ import Bluefin.Internal.OneWayCoercible ( OneWayCoercible (oneWayCoercibleImpl), OneWayCoercibleD,- OneWayCoercion, gOneWayCoercible, oneWayCoerce,- oneWayCoerceWith, oneWayCoercible, unsafeOneWayCoercible, )@@ -38,8 +36,8 @@ import Data.Kind (Type) import Data.Proxy (Proxy (Proxy)) import Data.Type.Coercion (Coercion (Coercion))-import GHC.Exts (Proxy#, proxy#)-import GHC.Generics (Generic, M1 (M1), Rec1 (Rec1), (:*:) ((:*:)))+import GHC.Exts (Any, Proxy#, proxy#)+import GHC.Generics (Generic, M1, Rec1, (:*:)) import System.IO.Unsafe (unsafePerformIO) import Unsafe.Coerce (unsafeCoerce) import Prelude hiding (drop, head, read, return)@@ -399,85 +397,136 @@ -- instance (e :> es) => 'OneWayCoercible' (Application e) (Application es) where -- oneWayCoercibleImpl = 'gOneWayCoercible' -- @+--+-- In occasional cases, @gOneWayCoercible@ won't work, in which can+-- you can instead use+--+-- @+-- instance (e :> es) => OneWayCoercible (MyHandle e) (MyHandle es) where+-- oneWayCoercibleImpl = 'oneWayCoercibleTrustMe' $ \\h -> \<mapHandle definition\>+-- @ class Handle (h :: Effects -> Type) where- {-# MINIMAL handleImpl | mapHandle #-}-- -- | Define @handleImpl@ using 'handleMapHandle'. handleImpl :: HandleD h- handleImpl = MkHandleD mapHandle - -- | Used to create compound effects, i.e. handles that contain- -- other handles.- --- -- You should not define this method in your type classes. Instead- -- define @handleImpl@. In a future version of Bluefin, @mapHandle@- -- will no longer be a method. If you previously had a definition- -- of @mapHandle@, for example like this:- --- -- @- -- instance Handle MyHandle where- -- mapHandle h = \<mapHandle definition\>- -- @- --- -- you should change it to- --- -- @- -- data MyHandle e = ...- -- deriving (Generic)- -- deriving (Handle) via 'OneWayCoercibleHandle' MyHandle- --- -- instance (e :> es) => 'OneWayCoercible' (MyHandle e) (MyHandle es) where- -- 'oneWayCoercibleImpl' = 'gOneWayCoercible'- -- @- --- -- If that doesn't work for any reason you can always reuse your old- -- definition of @mapHandle@ as follows. However,- -- 'handleMapHandle' will be removed at some point in the future, so- -- you can [open a new issue on- -- Bluefin](https://github.com/tomjaguarpaw/bluefin/issues/new) to- -- ask for advice.- --- -- @- -- instance Handle MyHandle where- -- handleImpl = handleMapHandle $ \\h -> \<mapHandle definition\>- -- @- mapHandle :: (e :> es) => h e -> h es- mapHandle = case handleImpl of MkHandleD f -> f+-- | This was previously a method of class 'Handle' using which you+-- could define @Handle@ instances. Now, you should define+-- @handleImpl@ instead. If you previously had a definition of+-- @mapHandle@, for example like this:+--+-- @+-- instance Handle MyHandle where+-- mapHandle h = \<mapHandle definition\>+-- @+--+-- you should change it to+--+-- @+-- data MyHandle e = ...+-- deriving (Generic)+-- deriving (Handle) via 'OneWayCoercibleHandle' MyHandle+--+-- instance (e :> es) => 'OneWayCoercible' (MyHandle e) (MyHandle es) where+-- 'oneWayCoercibleImpl' = 'gOneWayCoercible'+-- @+--+-- In occasional cases, @gOneWayCoercible@ won't work, in which can+-- you can instead use+--+-- @+-- instance (e :> es) => OneWayCoercible (MyHandle e) (MyHandle es) where+-- oneWayCoercibleImpl = 'oneWayCoercibleTrustMe' $ \\h -> \<mapHandle definition\>+-- @+mapHandle :: forall h e es. (Handle h, e :> es) => h e -> h es+mapHandle = case handleDictImpl @h of MkHandleDict -> oneWayCoerce +withHandle ::+ forall h r.+ (Handle h) =>+ ((forall e es. (e :> es) => OneWayCoercible (h e) (h es)) => r) ->+ r+withHandle r = case handleDictImpl @h of MkHandleDict -> r++type HandleDict :: (Effects -> Type) -> Type+data HandleDict h where+ MkHandleDict ::+ (forall e es. (e :> es) => OneWayCoercible (h e) (h es)) =>+ HandleDict h++type role HandleDict nominal++-- The essential properties of HandleD h are+--+-- (defining Handle' h =+-- forall e es. (e :> es) => OneWayCoercible (h e) (h es))+--+-- 1. It can be created by having Handle' h in scope+--+-- 2. Having it can put Handle' h into scope+--+-- 3. There is an instance+--+-- Coercible h1 h2 => Coercible (Handle' h1) (Handle' h2)+--+-- 1 is used by handleOneWayCoercible+--+-- 2 is used by withHandle+--+-- 3 is used by deriving via of OneWayCoercibleHandle+--+-- The only way I have worked out how to satisfy all three properties+-- is to have `HandleD h` not depend on `h`, instead have `Any` in+-- place of where we would use `h`. That way we can get property 3+-- (which requires cooperation with GHC) and achieve properties 1 and+-- 2 by unsafeCoerce.+ -- | The type of the 'handleImpl' method of the 'Handle' class.--- Create a @HandleD@ using 'handleMapHandle'.+-- Create a @HandleD@ using deriving via of 'OneWayCoercibleHandle'. type HandleD :: (Effects -> Type) -> Type-newtype HandleD h = MkHandleD (forall e es. (e :> es) => h e -> h es)+newtype HandleD h = MkHandleD (HandleDict Any) --- | For defining the 'handleImpl' method of the 'Handle' class.-handleMapHandle ::- (forall e es. (e :> es) => h e -> h es) ->- -- | ͘- HandleD h-handleMapHandle = MkHandleD+handleDictOfHandleD :: HandleD h -> HandleDict h+-- SPJ suggests this might be safe on ghc-devs+--+--https://mailman.haskell.org/archives/list/ghc-devs@haskell.org/thread/A4AJPPA3WSORHKCMFWAFX26XNQQVYT5R/+handleDictOfHandleD (MkHandleD f) = unsafeCoerce f +handleDictImpl :: (Handle h) => HandleDict h+handleDictImpl = handleDictOfHandleD handleImpl++type role HandleD representational+ handleOneWayCoercible :: forall h. (forall e es. (e :> es) => OneWayCoercible (h e) (h es)) => -- | ͘ HandleD h-handleOneWayCoercible = MkHandleD oneWayCoerce--handleOneWayCoercion ::- (forall e es. (e :> es) => OneWayCoercion (h e) (h es)) ->- -- | ͘- HandleD h-handleOneWayCoercion owc = MkHandleD (oneWayCoerceWith owc)+-- SPJ suggests this might be safe on ghc-devs+--+--https://mailman.haskell.org/archives/list/ghc-devs@haskell.org/thread/A4AJPPA3WSORHKCMFWAFX26XNQQVYT5R/+handleOneWayCoercible = MkHandleD (unsafeCoerce (MkHandleDict @h)) instance (Handle h) => Handle (Rec1 h) where- handleImpl = handleMapHandle $ \(Rec1 h) -> Rec1 (mapHandle h)+ handleImpl = withHandle @h handleOneWayCoercible instance (Handle h) => Handle (M1 i t h) where- handleImpl = handleMapHandle $ \(M1 h) -> M1 (mapHandle h)+ handleImpl = withHandle @h handleOneWayCoercible instance (Handle h1, Handle h2) => Handle (h1 :*: h2) where- handleImpl = handleMapHandle $ \(h1 :*: h2) -> mapHandle h1 :*: mapHandle h2+ handleImpl = withHandle @h1 (withHandle @h2 handleOneWayCoercible) +-- | It is not always possible to derive an instance of+-- 'OneWayCoercible'. In such cases write a definition of+-- 'oneWayCoercibleImpl' using @oneWayCoercibleTrustMe@. Its argument+-- must be a total function mapping the effect parameter of @h@.+-- Normally you'll write the function by mapping the constitients of+-- @h@ using the functions in this module like 'mapHandle', 'useImpl'+-- and 'useImplUnder'.+--+-- If this function is not total then it's possible you are violating+-- the type system, which could lead to segfaults or worse. It's not+-- easy to supply a partial function by accident, but be careful!+-- (N.B. it must not be literally @mapHandle@ otherwise you'll have a+-- circular definition!) oneWayCoercibleTrustMe :: (e :> es) => (forall e' es'. (e' :> es') => h e' -> h es') ->@@ -1582,6 +1631,7 @@ (\() -> k) newtype HandleReader h e = UnsafeMkHandleReader (State (h e) e)+ deriving (Handle) via OneWayCoercibleHandle (HandleReader h) -- In general this is really tremendously unsafe because we could take -- an `HandleReader h e`, map it to `HandleReader h es`, write an `h@@ -1644,11 +1694,8 @@ useImplIn k h' -instance (Handle h) => Handle (HandleReader h) where- -- This handleImpl is now a special case that doesn't use- -- OneWayCoercion. We can only fix that when we store a- -- OneWayCoercion inside `Handle`.- handleImpl = handleMapHandle mapHandleReader+instance (e :> es) => OneWayCoercible (HandleReader h e) (HandleReader h es) where+ oneWayCoercibleImpl = unsafeOneWayCoercible newtype ConstEffect r (e :: Effects) = MkConstEffect r deriving (Handle) via OneWayCoercibleHandle (ConstEffect r)
src/Bluefin/Internal/CloneableHandle.hs view
@@ -216,9 +216,13 @@ -- a 'CloneableHandle' instance for your type, as long as it is a -- product type of @CloneableHandle@s. newtype GenericCloneableHandle h e = MkGenericCloneableHandle (h e)+ deriving newtype (Handle) -instance (Handle h) => Handle (GenericCloneableHandle h) where- handleImpl = handleMapHandle $ \(MkGenericCloneableHandle h) ->+instance+ (Handle h, e :> es) =>+ OneWayCoercible (GenericCloneableHandle h e) (GenericCloneableHandle h es)+ where+ oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \(MkGenericCloneableHandle h) -> MkGenericCloneableHandle (mapHandle h) instance
src/Bluefin/Internal/Examples.hs view
@@ -6,9 +6,6 @@ import Bluefin.Internal hiding (b, w) import Bluefin.Internal.OneWayCoercible- ( OneWayCoercible (oneWayCoercibleImpl),- gOneWayCoercible,- ) import Bluefin.Internal.Pipes ( Producer, runEffect,@@ -25,7 +22,6 @@ import Data.Foldable (for_) import Data.Monoid (Any (Any, getAny)) import Data.Proxy (Proxy (Proxy))-import GHC.Generics (Generic) import Text.Read (readMaybe) import Prelude hiding ( break,@@ -791,9 +787,13 @@ counter7State :: State Int e, counter7Stream :: Stream String e }+ deriving (Handle) via OneWayCoercibleHandle Counter7 -instance Handle Counter7 where- handleImpl = handleMapHandle $ \c ->+-- | The "forall" in the type of @incCounter7@ means that we can't+-- derive the @OneWayCoercible@ instance with 'gOneWayCoercible' so+-- instead we use @oneWayCoercibleTrustMe@.+instance (e :> es) => OneWayCoercible (Counter7 e) (Counter7 es) where+ oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \c -> MkCounter7 { incCounter7Impl = \ex -> useImplUnder (incCounter7Impl c ex), counter7State = mapHandle (counter7State c),@@ -1036,13 +1036,20 @@ Left e -> "Caught IOException:\n" ++ show e Right contents -> contents +-- | The "forall" in the type of @localRImpl@ means that we can't+-- derive the @OneWayCoercible@ instance with 'gOneWayCoercible' so+-- instead we use @oneWayCoercibleTrustMe@. data DynamicReader r e = DynamicReader { askLRImpl :: Eff e r, localLRImpl :: forall e' a. (r -> r) -> Eff e' a -> Eff (e' :& e) a }+ deriving (Handle) via OneWayCoercibleHandle (DynamicReader r) -instance Handle (DynamicReader r) where- handleImpl = handleMapHandle $ \h ->+instance+ (e :> es) =>+ OneWayCoercible (DynamicReader r e) (DynamicReader r es)+ where+ oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \h -> DynamicReader { askLRImpl = useImpl (askLRImpl h), localLRImpl = \f k -> useImplUnder (localLRImpl h f k)
src/Bluefin/Internal/System/IO.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DerivingVia #-}+ module Bluefin.Internal.System.IO ( module Bluefin.Internal.System.IO, System.IO.IOMode (..),@@ -9,21 +11,25 @@ IOE, bracket, effIO,- handleMapHandle, mapHandle,+ oneWayCoercibleTrustMe, useImplIn, (:&), (:>), ) import Bluefin.Internal qualified+import Bluefin.Internal.OneWayCoercible (OneWayCoercible (oneWayCoercibleImpl)) import System.IO qualified -- We can probably get away without the IOE and just use -- unsafeProvideIO on all Handle functions data Handle e = UnsafeMkHandle System.IO.Handle (IOE e)+ deriving+ (Bluefin.Internal.Handle)+ via Bluefin.Internal.OneWayCoercibleHandle Handle -instance Bluefin.Internal.Handle Handle where- handleImpl = handleMapHandle $ \(UnsafeMkHandle h io) ->+instance (e :> es) => OneWayCoercible (Handle e) (Handle es) where+ oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \(UnsafeMkHandle h io) -> UnsafeMkHandle h (mapHandle io) withFile ::