diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,55 @@
+1.2.2
+-----
+* Relaxed upper version bound for `transformers`.
+
+1.2.1
+-----
+* Explicitly mark `Data.StateVar` as Safe (or Trustworthy for GHC before 7.10).
+
+1.2
+---
+* Added instances for `ForeignPtr`.
+
+1.1.1.1
+-------
+* Relaxed upper version bound for `stm`.
+
+1.1.1.0
+-------
+* Track recent move of `Contravariant` to `base`.
+
+1.1.0.4
+-------
+* Corrected HasUpdate's superclass constraint.
+
+1.1.0.3
+-------
+* Removed a couple of redundant typeclass constraints.
+
+1.1.0.2
+-------
+* Relaxed upper version bound for `transformers`.
+
+1.1.0.1
+-------
+* Documentation changes only.
+
+1.1.0.0
+-------
+* Melded the API of `foreign-var` 0.1 with the API of `StateVar` 1.0.1.1
+* Introduced `HasUpdate`, which permits a wider array of uses of these combinators, including usecases that must update atomically.
+* Switched to multi-parameter typeclasses. This permits `Ptr a` to be directly employed as an instance of `HasGetter`, `HasUpdate`, and `HasSetter`.
+
+1.0.1.1
+-------
+* Infrastructure changes only.
+
+1.0.1.0
+-------
+* Exposed `GettableStateVar`, `SettableStateVar` and `StateVar` constructors to make writing own instances possible.
+* Added `Functor`, `Applicative` and `Monad` instances for `GettableStateVar`.
+* Various infrastructure improvements.
+
+1.0.0.0
+-------
+* Initial release.
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
-Copyright (c) 2009, Sven Panne
+Copyright (c) 2014-2015, Edward Kmett
+Copyright (c) 2009-2021, Sven Panne
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+[![Hackage](https://img.shields.io/hackage/v/StateVar.svg)](https://hackage.haskell.org/package/StateVar)
+[![Stackage LTS](https://www.stackage.org/package/StateVar/badge/lts)](https://www.stackage.org/lts/package/StateVar)
+[![Stackage nightly](https://www.stackage.org/package/StateVar/badge/nightly)](https://www.stackage.org/nightly/package/StateVar)
+[![Build Status](https://img.shields.io/travis/haskell-opengl/StateVar/master.svg)](https://travis-ci.org/haskell-opengl/StateVar)
diff --git a/StateVar.cabal b/StateVar.cabal
--- a/StateVar.cabal
+++ b/StateVar.cabal
@@ -1,25 +1,68 @@
 name: StateVar
-version: 1.0.1.1
+version: 1.2.2
 synopsis: State variables
 description:
   This package contains state variables, which are references in the IO monad,
   like IORefs or parts of the OpenGL state.
 homepage: https://github.com/haskell-opengl/StateVar
 bug-reports: https://github.com/haskell-opengl/StateVar/issues
+copyright: Copyright (C) 2014-2015 Edward A. Kmett, 2009-2021 Sven Panne
 license: BSD3
 license-file: LICENSE
-author: Sven Panne
+author: Sven Panne and Edward Kmett
 maintainer: Sven Panne <svenpanne@gmail.com>
 category: Data
 build-type: Simple
-cabal-version: >=1.10
+tested-with:
+  GHC == 7.0.4
+  GHC == 7.2.2
+  GHC == 7.4.2
+  GHC == 7.6.3
+  GHC == 7.8.4
+  GHC == 7.10.3
+  GHC == 8.0.2
+  GHC == 8.2.2
+  GHC == 8.4.3
+  GHC == 8.6.5
+  GHC == 8.8.4
+  GHC == 8.10.3
+  GHC == 8.10.4
+  GHC == 9.0.1
+cabal-version: >= 1.10
+extra-source-files:
+  README.md
+  CHANGELOG.md
 
 library
-  exposed-modules: Data.StateVar
-  build-depends: base >= 3 && < 5
+  exposed-modules:
+    Data.StateVar
+
+  build-depends:
+    base         >= 4   && < 5,
+    stm          >= 2.3.0.1 && < 2.6,
+    transformers >= 0.3 && < 0.7
+
   default-language: Haskell2010
-  hs-Source-Dirs: src
+  other-extensions:
+    CPP
+    DeriveDataTypeable
+    MultiParamTypeClasses
+    FunctionalDependencies
+    FlexibleInstances
+    TypeFamilies
+
+  hs-source-dirs: src
   ghc-options: -Wall
+  if impl(ghc > 8)
+    ghc-options: -Wcompat
+  if impl(ghc>=7.4)
+    -- other-extensions: DefaultSignatures
+    cpp-options: -DUSE_DEFAULT_SIGNATURES=1
+
+  if impl(ghc >= 9.0)
+    -- these flags may abort compilation with GHC-8.10
+    -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/3295
+    ghc-options: -Winferred-safe-imports -Wmissing-safe-haskell-mode
 
 source-repository head
   type: git
diff --git a/src/Data/StateVar.hs b/src/Data/StateVar.hs
--- a/src/Data/StateVar.hs
+++ b/src/Data/StateVar.hs
@@ -1,7 +1,22 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE FlexibleInstances #-}
+#if USE_DEFAULT_SIGNATURES
+{-# LANGUAGE DefaultSignatures #-}
+#endif
+{-# LANGUAGE TypeFamilies #-}
+-- Foreign.ForeignPtr is unsafe before GHC-7.10
+#if __GLASGOW_HASKELL__ >= 704 && MIN_VERSION_base(4,8,0)
+{-# LANGUAGE Safe #-}
+#elif __GLASGOW_HASKELL__ >= 702
+{-# LANGUAGE Trustworthy #-}
+#endif
 --------------------------------------------------------------------------------
 -- |
 -- Module      :  Data.StateVar
--- Copyright   :  (c) Sven Panne 2014
+-- Copyright   :  (c) Edward Kmett 2014-2019, Sven Panne 2009-2021
 -- License     :  BSD3
 -- 
 -- Maintainer  :  Sven Panne <svenpanne@gmail.com>
@@ -12,7 +27,7 @@
 -- the OpenGL state. Note that state variables are not neccessarily writable or
 -- readable, they may come in read-only or write-only flavours, too. As a very
 -- simple example for a state variable, consider an explicitly allocated memory
--- buffer. This buffer can easily be converted into a 'StateVar':
+-- buffer. This buffer could easily be converted into a 'StateVar':
 --
 -- @
 -- makeStateVarFromPtr :: Storable a => Ptr a -> StateVar a
@@ -32,6 +47,16 @@
 --      print x
 -- @
 --
+-- However, 'Ptr' can be used directly through the same API:
+--
+-- @
+--   do p <- malloc :: IO (Ptr Int)
+--      p $= 11
+--      p $~ (+ 22)
+--      x <- get p
+--      print x
+-- @
+--
 -- 'IORef's are state variables, too, so an example with them looks extremely
 -- similiar:
 --
@@ -44,117 +69,237 @@
 -- @
 --------------------------------------------------------------------------------
 
-module Data.StateVar (
-   -- * Readable State Variables
-   HasGetter(..),
-   GettableStateVar(..), makeGettableStateVar,
-   -- * Writable State Variables
-   HasSetter(..),
-   SettableStateVar(..), makeSettableStateVar,
-   -- * General State Variables
-   StateVar(..), makeStateVar,
-   -- * Utility Functions
-   ($~), ($=!), ($~!)
-) where
+module Data.StateVar
+  (
+  -- * Readable State Variables
+    HasGetter(get)
+  , GettableStateVar, makeGettableStateVar
+  -- * Writable State Variables
+  , HasSetter(($=)), ($=!)
+  , SettableStateVar(SettableStateVar), makeSettableStateVar
+  -- * Updatable State Variables
+  , HasUpdate(($~), ($~!))
+  , StateVar(StateVar), makeStateVar
+  , mapStateVar
+  ) where
 
-import Control.Applicative ( Applicative(..) )
-import Data.IORef ( IORef, readIORef, writeIORef )
+import Control.Concurrent.STM
+import Control.Monad.IO.Class
+import Data.IORef
+import Data.Typeable
+import Foreign.ForeignPtr
+import Foreign.Ptr
+import Foreign.Storable
+#if MIN_VERSION_base(4,12,0)
+import Data.Functor.Contravariant
+#endif
 
---------------------------------------------------------------------------------
+--------------------------------------------------------------------
+-- * StateVar
+--------------------------------------------------------------------
 
-infixr 2 $=
+-- | A concrete implementation of a readable and writable state variable,
+-- carrying one IO action to read the value and another IO action to write the
+-- new value. This data type represents a piece of mutable, imperative state
+-- with possible side-effects. These tend to encapsulate all sorts tricky
+-- behavior in external libraries, and may well throw exceptions. Inhabitants
+-- __should__ satsify the following properties:
+--
+-- * In the absence of concurrent mutation from other threads or a thrown
+-- exception:
+--
+-- @
+-- do x <- 'get' v; v '$=' y; v '$=' x
+-- @
+--
+-- should restore the previous state.
+--
+-- * Ideally, in the absence of thrown exceptions:
+--
+-- @
+-- v '$=' a >> 'get' v
+-- @
+--
+-- should return @a@, regardless of @a@. In practice some 'StateVar's only
+-- permit a very limited range of value assignments, and do not report failure.
+data StateVar a = StateVar (IO a) (a -> IO ()) deriving Typeable
 
---------------------------------------------------------------------------------
+#if MIN_VERSION_base(4,12,0)
+instance Contravariant SettableStateVar where
+  contramap f (SettableStateVar k) = SettableStateVar (k . f)
+  {-# INLINE contramap #-}
+#endif
 
--- | The class of all readable state variables.
-class HasGetter g where
-   -- | Read the value of a state variable.
-   get :: g a -> IO a
+-- | Construct a 'StateVar' from two IO actions, one for reading and one for
+--- writing.
+makeStateVar
+  :: IO a         -- ^ getter
+  -> (a -> IO ()) -- ^ setter
+  -> StateVar a
+makeStateVar = StateVar
 
-instance HasGetter IORef where
-   get = readIORef
+-- | Change the type of a 'StateVar'
+mapStateVar :: (b -> a) -> (a -> b) -> StateVar a -> StateVar b
+mapStateVar ba ab (StateVar ga sa) = StateVar (fmap ab ga) (sa . ba)
+{-# INLINE mapStateVar #-}
 
--- | A concrete implementation of a read-only state variable, carrying an IO
+-- | A concrete implementation of a write-only state variable, carrying an IO
+-- action to write the new value.
+newtype SettableStateVar a = SettableStateVar (a -> IO ())
+  deriving Typeable
+
+-- | Construct a 'SettableStateVar' from an IO action for writing.
+makeSettableStateVar
+  :: (a -> IO ()) -- ^ setter
+  -> SettableStateVar a
+makeSettableStateVar = SettableStateVar
+{-# INLINE makeSettableStateVar #-}
+
+-- | A concrete implementation of a read-only state variable is simply an IO
 -- action to read the value.
-newtype GettableStateVar a = GettableStateVar (IO a)
+type GettableStateVar = IO
 
-instance HasGetter GettableStateVar where
-   get (GettableStateVar g) = g
+-- | Construct a 'GettableStateVar' from an IO action.
+makeGettableStateVar
+  :: IO a -- ^ getter
+  -> GettableStateVar a
+makeGettableStateVar = id
+{-# INLINE makeGettableStateVar #-}
 
-instance Functor GettableStateVar where
-  fmap f (GettableStateVar g) = GettableStateVar (fmap f g)
+--------------------------------------------------------------------
+-- * HasSetter
+--------------------------------------------------------------------
 
-instance Applicative GettableStateVar where
-  pure = GettableStateVar . pure
-  GettableStateVar g <*> GettableStateVar h = GettableStateVar (g <*> h)
+infixr 2 $=, $=!
 
-instance Monad GettableStateVar where
-  return = pure
-  GettableStateVar g >>= h = GettableStateVar (g >>= get . h)
+-- | This is the class of all writable state variables.
+class HasSetter t a | t -> a where
+  -- | Write a new value into a state variable.
+  ($=) :: MonadIO m => t -> a -> m ()
 
--- | Construct a 'GettableStateVar' from an IO action.
-makeGettableStateVar :: IO a -> GettableStateVar a
-makeGettableStateVar = GettableStateVar
+-- | This is a variant of '$=' which is strict in the value to be set.
+($=!) :: (HasSetter t a, MonadIO m) => t -> a -> m ()
+p $=! a = (p $=) $! a
+{-# INLINE ($=!) #-}
 
---------------------------------------------------------------------------------
+instance HasSetter (SettableStateVar a) a where
+  SettableStateVar f $= a = liftIO (f a)
+  {-# INLINE ($=) #-}
 
--- | The class of all writable state variables.
-class HasSetter s where
-   -- | Write a new value into a state variable.
-   ($=) :: s a -> a -> IO ()
+instance HasSetter (StateVar a) a where
+  StateVar _ s $= a = liftIO $ s a
+  {-# INLINE ($=) #-}
 
-instance HasSetter IORef where
-   ($=) = writeIORef
+instance Storable a => HasSetter (Ptr a) a where
+  p $= a = liftIO $ poke p a
+  {-# INLINE ($=) #-}
 
--- | A concrete implementation of a write-only state variable, carrying an IO
--- action to write the new value.
-newtype SettableStateVar a = SettableStateVar (a -> IO ())
+instance HasSetter (IORef a) a where
+  p $= a = liftIO $ writeIORef p a
+  {-# INLINE ($=) #-}
 
-instance HasSetter SettableStateVar where
-   ($=) (SettableStateVar s) a = s a
+instance HasSetter (TVar a) a where
+  p $= a = liftIO $ atomically $ writeTVar p a
+  {-# INLINE ($=) #-}
 
--- instance Contravariant SettableStateVar where
---    contramap f (SettableStateVar s) = SettableStateVar (s . f)
+instance Storable a => HasSetter (ForeignPtr a) a where
+  p $= a = liftIO $ withForeignPtr p ($= a)
+  {-# INLINE ($=) #-}
 
--- | Construct a 'SettableStateVar' from an IO action.
-makeSettableStateVar :: (a -> IO ()) -> SettableStateVar a
-makeSettableStateVar = SettableStateVar
+--------------------------------------------------------------------
+-- * HasUpdate
+--------------------------------------------------------------------
 
---------------------------------------------------------------------------------
+infixr 2 $~, $~!
 
--- | A concrete implementation of a readable and writable state variable,
--- carrying one IO action to read the value and another IO action to write the
--- new value.
-data StateVar a =
-   StateVar (GettableStateVar a) (SettableStateVar a)
+-- | This is the class of all updatable state variables.
+class HasSetter t b => HasUpdate t a b | t -> a b where
+  -- | Transform the contents of a state variable with a given funtion.
+  ($~) :: MonadIO m => t -> (a -> b) -> m ()
+#if USE_DEFAULT_SIGNATURES
+  default ($~) :: (MonadIO m, a ~ b, HasGetter t a) => t -> (a -> b) -> m ()
+  ($~) = defaultUpdate
+#endif
+  -- | This is a variant of '$~' which is strict in the transformed value.
+  ($~!) :: MonadIO m => t -> (a -> b) -> m ()
+#if USE_DEFAULT_SIGNATURES
+  default ($~!) :: (MonadIO m, a ~ b, HasGetter t a) => t -> (a -> b) -> m ()
+  ($~!) = defaultUpdateStrict
+#endif
 
-instance HasGetter StateVar where
-   get (StateVar g _) = get g
+defaultUpdate :: (MonadIO m, a ~ b, HasGetter t a, HasSetter t a) => t -> (a -> b) -> m ()
+defaultUpdate r f = liftIO $ do
+  a <- get r
+  r $= f a
 
-instance HasSetter StateVar where
-   ($=) (StateVar _ s) a = s $= a
+defaultUpdateStrict :: (MonadIO m, a ~ b, HasGetter t a, HasSetter t a) => t -> (a -> b) -> m ()
+defaultUpdateStrict r f = liftIO $ do
+  a <- get r
+  r $=! f a
 
--- | Construct a 'StateVar' from two IO actions, one for reading and one for
--- writing.
-makeStateVar :: IO a -> (a -> IO ()) -> StateVar a
-makeStateVar g s = StateVar (makeGettableStateVar g) (makeSettableStateVar s)
+instance HasUpdate (StateVar a) a a where
+  ($~) = defaultUpdate
+  ($~!) = defaultUpdateStrict
 
---------------------------------------------------------------------------------
+instance Storable a => HasUpdate (Ptr a) a a where
+  ($~) = defaultUpdate
+  ($~!) = defaultUpdateStrict
 
--- | A modificator convenience function, transforming the contents of a state
--- variable with a given funtion.
+instance HasUpdate (IORef a) a a where
+  r $~ f  = liftIO $ atomicModifyIORef r $ \a -> (f a,())
+#if MIN_VERSION_base(4,6,0)
+  r $~! f = liftIO $ atomicModifyIORef' r $ \a -> (f a,())
+#else
+  r $~! f = liftIO $ do
+    s <- atomicModifyIORef r $ \a -> let s = f a in (s, s)
+    s `seq` return ()
+#endif
 
-($~) :: (HasGetter v, HasSetter v) => v a -> (a -> a) -> IO ()
-v $~ f = do
-   x <- get v
-   v $= f x
+instance HasUpdate (TVar a) a a where
+  r $~ f = liftIO $ atomically $ do
+    a <- readTVar r
+    writeTVar r (f a)
+  r $~! f = liftIO $ atomically $ do
+    a <- readTVar r
+    writeTVar r $! f a
 
--- | A variant of '$=' which is strict in the value to be set.
-($=!) :: HasSetter s => s a -> a -> IO ()
-v $=! x = x `seq` v $= x
+instance Storable a => HasUpdate (ForeignPtr a) a a where
+  p $~ f = liftIO $ withForeignPtr p ($~ f)
+  p $~! f = liftIO $ withForeignPtr p ($~! f)
 
--- | A variant of '$~' which is strict in the transformed value.
-($~!) :: (HasGetter v, HasSetter v) => v a -> (a -> a) -> IO ()
-v $~! f = do
-   x <- get v
-   v $=! f x
+--------------------------------------------------------------------
+-- * HasGetter
+--------------------------------------------------------------------
+
+-- | This is the class of all readable state variables.
+class HasGetter t a | t -> a where
+  get :: MonadIO m => t -> m a
+
+instance HasGetter (StateVar a) a where
+  get (StateVar g _) = liftIO g
+  {-# INLINE get #-}
+
+instance HasGetter (TVar a) a where
+  get = liftIO . atomically . readTVar
+  {-# INLINE get #-}
+
+instance HasGetter (IO a) a where
+  get = liftIO
+  {-# INLINE get #-}
+
+instance HasGetter (STM a) a where
+  get = liftIO . atomically
+  {-# INLINE get #-}
+
+instance Storable a => HasGetter (Ptr a) a where
+  get = liftIO . peek
+  {-# INLINE get #-}
+
+instance HasGetter (IORef a) a where
+  get = liftIO . readIORef
+  {-# INLINE get #-}
+
+instance Storable a => HasGetter (ForeignPtr a) a where
+  get p = liftIO $ withForeignPtr p get
+  {-# INLINE get #-}
+
