Skip to content

Commit fd41b78

Browse files
authored
feat(Internal): add Scoped (codensity) (#167)
An implementation of `Codensity` called `Scoped` to clean up nested CPS code along with some common `Foreign.*` functions wrapped in `Scoped`.
1 parent 30e7abe commit fd41b78

File tree

4 files changed

+141
-3
lines changed

4 files changed

+141
-3
lines changed

cabal.project

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ packages:
55
package libsodium-bindings
66
ghc-options: -Werror
77

8+
package sel
9+
ghc-options: -Werror
10+
811
package *
912
ghc-options: -haddock
10-
documentation: True
1113

1214
test-show-details: direct
1315
tests: True
16+
documentation: True

sel/sel.cabal

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 3.0
22
name: sel
3-
version: 0.0.2.0
3+
version: 0.0.3.0
44
category: Cryptography
55
synopsis: Cryptography for the casual user
66
description:
@@ -62,14 +62,19 @@ library
6262
Sel.SecretKey.Cipher
6363
Sel.SecretKey.Stream
6464

65-
other-modules: Sel.Internal
65+
other-modules:
66+
Sel.Internal
67+
Sel.Internal.Scoped
68+
Sel.Internal.Scoped.Foreign
69+
6670
build-depends:
6771
, base >=4.14 && <5
6872
, base16 ^>=1.0
6973
, bytestring >=0.10 && <0.13
7074
, libsodium-bindings ^>=0.0.2
7175
, text >=1.2 && <2.2
7276
, text-display ^>=0.0
77+
, transformers ^>=0.6.0
7378

7479
test-suite sel-tests
7580
import: common

sel/src/Sel/Internal/Scoped.hs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{-# LANGUAGE InstanceSigs #-}
2+
{-# LANGUAGE PolyKinds #-}
3+
{-# LANGUAGE RankNTypes #-}
4+
{-# LANGUAGE StandaloneKindSignatures #-}
5+
{-# LANGUAGE TypeFamilies #-}
6+
{-# LANGUAGE TypeOperators #-}
7+
{-# LANGUAGE UndecidableInstances #-}
8+
9+
-- |
10+
-- Module : Sel.Internal.Scoped
11+
-- Description : Continuation-passing utilities
12+
-- Copyright : (c) Jack Henahan, 2024
13+
-- License : BSD-3-Clause
14+
-- Maintainer : The Haskell Cryptography Group
15+
-- Portability : GHC only
16+
--
17+
-- This module implements a version of @Codensity@, modeling delimited
18+
-- continuations. Useful for avoiding extreme rightward drift in
19+
-- chains of @withForeignPtr@ and friends.
20+
module Sel.Internal.Scoped where
21+
22+
import Control.Monad (ap, void)
23+
import Control.Monad.IO.Class (MonadIO (liftIO))
24+
import Control.Monad.Trans.Class (MonadTrans (lift))
25+
import Data.Kind (Type)
26+
import Data.Type.Equality (type (~~))
27+
import GHC.Exts (RuntimeRep, TYPE)
28+
29+
-- | @since 0.0.3.0
30+
type Scoped :: forall {k} {rep :: RuntimeRep}. (k -> TYPE rep) -> Type -> Type
31+
newtype Scoped m a = Scoped {runScoped :: forall b. (a -> m b) -> m b}
32+
33+
-- | @since 0.0.3.0
34+
instance Functor (Scoped f) where
35+
fmap f (Scoped m) = Scoped $ \k -> m (k . f)
36+
{-# INLINE fmap #-}
37+
38+
-- | @since 0.0.3.0
39+
instance Applicative (Scoped f) where
40+
pure a = Scoped $ \k -> k a
41+
{-# INLINE pure #-}
42+
43+
(<*>) = ap
44+
{-# INLINE (<*>) #-}
45+
46+
-- | @since 0.0.3.0
47+
instance Monad (Scoped f) where
48+
Scoped m >>= f = Scoped $ \k ->
49+
m $ \a -> runScoped (f a) k
50+
{-# INLINE (>>=) #-}
51+
52+
-- | @since 0.0.3.0
53+
instance (MonadIO m', m' ~~ m) => MonadIO (Scoped m) where
54+
liftIO = lift . liftIO
55+
{-# INLINE liftIO #-}
56+
57+
-- | @since 0.0.3.0
58+
instance MonadTrans Scoped where
59+
lift m = Scoped (m >>=)
60+
{-# INLINE lift #-}
61+
62+
-- | @since 0.0.3.0
63+
reset :: Monad m => Scoped m a -> Scoped m a
64+
reset = lift . use
65+
66+
-- | @since 0.0.3.0
67+
shift :: Applicative m => (forall b. (a -> m b) -> Scoped m b) -> Scoped m a
68+
shift f = Scoped $ use . f
69+
70+
-- | @since 0.0.3.0
71+
use :: Applicative m => Scoped m a -> m a
72+
use (Scoped m) = m pure
73+
74+
-- | @since 0.0.3.0
75+
useM :: Monad m => Scoped m (m a) -> m a
76+
useM f = use $ f >>= lift
77+
78+
-- | @since 0.0.3.0
79+
use_ :: Applicative m => Scoped m a -> m ()
80+
use_ = void . use
81+
82+
-- | @since 0.0.3.0
83+
useM_ :: Monad m => Scoped m (m a) -> m ()
84+
useM_ = void . useM
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{-# LANGUAGE ImportQualifiedPost #-}
2+
3+
-- |
4+
-- Module : Sel.Internal.Scoped.Foreign
5+
-- Description : Scoped wrappers around pointer manipulation
6+
-- Copyright : (c) Jack Henahan, 2024
7+
-- License : BSD-3-Clause
8+
-- Maintainer : The Haskell Cryptography Group
9+
-- Portability : GHC only
10+
--
11+
-- This module wraps some common points of contact with 'Ptr',
12+
-- 'ForeignPtr', and friends up in 'Scoped' for the sake of not saying
13+
-- 'lift' absolutely everywhere.
14+
module Sel.Internal.Scoped.Foreign where
15+
16+
import Control.Monad.Trans.Class (lift)
17+
import Data.ByteString (StrictByteString)
18+
import Data.ByteString.Unsafe qualified as ByteString
19+
import Foreign (ForeignPtr, Ptr, Storable)
20+
import Foreign qualified
21+
import Foreign.C (CString, CStringLen)
22+
import Sel.Internal.Scoped
23+
24+
-- | @since 0.0.3.0
25+
foreignPtr :: ForeignPtr a -> Scoped IO (Ptr a)
26+
foreignPtr fptr = Scoped $ Foreign.withForeignPtr fptr
27+
28+
-- | @since 0.0.3.0
29+
unsafeCStringLen :: StrictByteString -> Scoped IO CStringLen
30+
unsafeCStringLen bs = Scoped $ ByteString.unsafeUseAsCStringLen bs
31+
32+
-- | @since 0.0.3.0
33+
unsafeCString :: StrictByteString -> Scoped IO CString
34+
unsafeCString bs = Scoped $ ByteString.unsafeUseAsCString bs
35+
36+
-- | @since 0.0.3.0
37+
mallocBytes :: Int -> Scoped IO (Ptr a)
38+
mallocBytes = lift . Foreign.mallocBytes
39+
40+
-- | @since 0.0.3.0
41+
mallocForeignPtrBytes :: Int -> Scoped IO (ForeignPtr a)
42+
mallocForeignPtrBytes len = lift $ Foreign.mallocForeignPtrBytes len
43+
44+
-- | @since 0.0.3.0
45+
copyArray :: Storable a => Ptr a -> Ptr a -> Int -> Scoped IO ()
46+
copyArray target source len = lift $ Foreign.copyArray target source len

0 commit comments

Comments
 (0)