diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for aws-arn
 
+## 0.3.0.0 -- 2022-10-14
+
+* Reinstate the improper `Iso'`s from version 0.1.x; the 0.2.0.0
+  lenses were also unlawful and the `Iso'`s are more ergonomic.
+
 ## 0.2.0.0 -- 2022-07-19
 
 * Rename `fromFoo`/`toFoo` to `renderFoo`/`parseFoo`:
diff --git a/aws-arn.cabal b/aws-arn.cabal
--- a/aws-arn.cabal
+++ b/aws-arn.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               aws-arn
-version:            0.2.0.0
+version:            0.3.0.0
 synopsis:
   Types and optics for manipulating Amazon Resource Names (ARNs)
 
diff --git a/src/Network/AWS/ARN.hs b/src/Network/AWS/ARN.hs
--- a/src/Network/AWS/ARN.hs
+++ b/src/Network/AWS/ARN.hs
@@ -60,21 +60,24 @@
 import Data.Eq.Deriving (deriveEq1)
 import Data.Hashable (Hashable)
 import Data.Hashable.Lifted (Hashable1)
-import Data.List.NonEmpty (NonEmpty)
-import qualified Data.List.NonEmpty as NonEmpty
 import Data.Ord.Deriving (deriveOrd1)
 import Data.Text (Text)
 import qualified Data.Text as T
 import GHC.Generics (Generic, Generic1)
-import Network.AWS.ARN.Internal.Lens (Lens', Prism', prism')
+import Network.AWS.ARN.Internal.Lens (Iso', Prism', iso, prism')
 import Text.Show.Deriving (deriveShow1)
 
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Data.Function ((&))
+-- >>> import Network.AWS.ARN.Internal.Lens (from, ix, (.~), (^.), (^?))
+
 -- | A parsed ARN. Either use the '_ARN' 'Prism'', or the 'parseARN' and
 -- 'renderARN' functions to convert @'Text' \<-\> 'ARN'@.  The
 -- 'resource' part of an ARN will often contain colon- or
 -- slash-separated parts which precisely identify some resource. If
 -- there is no service-specific module (see below), the 'colons' and
--- 'slashes' lenses in this module can pick apart the `resource`
+-- 'slashes' optics in this module can pick apart the `resource`
 -- field.
 --
 -- If you want lenses into individual fields, use the
@@ -165,35 +168,41 @@
 
 -- | Split a 'Text' into colon-separated parts.
 --
--- This is useful for editing the resource part of an ARN:
+-- This is an improper 'Iso'' (@Text.intercalate ":" . Text.splitOn
+-- ":" = id@, but @Text.splitOn ":" . Text.intercalate ":" /= id@).
+-- This causes violations of the 'Iso'' laws for lists whose members
+-- contain @\':\'@:
 --
+-- >>> [":"] ^. from colons . colons
+-- ["",""]
+--
+-- The laws are also violated on empty lists:
+--
+-- >>> [] ^. from colons . colons
+-- [""]
+--
+-- Nevertheless, it is still useful:
+--
 -- >>> "foo:bar:baz" & colons . ix 1 .~ "quux"
 -- "foo:quux:baz"
 --
--- Writing back through the lens ignores the string it is applied to:
---
--- >>> "Hello, world!" & colons .~ "dude" :| ["sweet"]
--- "dude:sweet"
+-- Ed [discusses improper
+-- optics](https://old.reddit.com/r/haskell/comments/32xva8/the_laws_of_asymmetric_wellbehaved_lenses_are/cqhq1gk/)
+-- in an old Reddit comment.
 --
--- @since 0.2.0.0
-colons :: Lens' Text (NonEmpty Text)
-colons = splitOn ":"
+-- @since 0.3.0.0
+colons :: Iso' Text [Text]
+colons = iso (T.splitOn ":") (T.intercalate ":")
 {-# INLINE colons #-}
 
 -- | Split a 'Text' into slash-separated parts.
 --
--- >>> "foo/bar/baz" ^. slashes
--- "foo" :| ["bar","baz"]
+-- List 'colons', this is an improper 'Iso'', but it is still useful:
 --
--- Similar caveats to 'colons' apply.
+-- >>> "foo/bar/baz" ^. slashes
+-- ["foo","bar","baz"]
 --
--- @since 0.2.0.0
-slashes :: Lens' Text (NonEmpty Text)
-slashes = splitOn "/"
+-- @since 0.3.0.0
+slashes :: Iso' Text [Text]
+slashes = iso (T.splitOn "/") (T.intercalate "/")
 {-# INLINE slashes #-}
-
-splitOn :: Text -> Lens' Text (NonEmpty Text)
-splitOn delim f t =
-  T.intercalate delim . NonEmpty.toList
-    <$> f (NonEmpty.fromList (T.splitOn delim t))
-{-# INLINE splitOn #-}
diff --git a/src/Network/AWS/ARN/Internal/Lens.hs b/src/Network/AWS/ARN/Internal/Lens.hs
--- a/src/Network/AWS/ARN/Internal/Lens.hs
+++ b/src/Network/AWS/ARN/Internal/Lens.hs
@@ -16,33 +16,45 @@
 import Data.Functor.Const (Const (..))
 import Data.Functor.Identity (Identity (..))
 import Data.Monoid (First (..))
-import Data.Profunctor (dimap)
+import Data.Profunctor (Profunctor (..))
 import Data.Profunctor.Choice (Choice (..))
 import Data.Tagged (Tagged (..))
 
 type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s
 
+type Getting r s a = (a -> Const r a) -> s -> Const r s
+
 type Setter s a = (a -> Identity a) -> s -> Identity s
 
 set :: Setter s a -> a -> s -> s
 set l = over l . const
 {-# INLINE set #-}
 
+(.~) :: Setter s a -> a -> s -> s
+(.~) = set
+
+infixr 4 .~
+
 over :: Setter s a -> (a -> a) -> s -> s
 over l f = runIdentity . l (Identity . f)
 {-# INLINE over #-}
 
+(^.) :: s -> Getting a s a -> a
+s ^. l = getConst $ l Const s
+
+infixl 8 ^.
+
 type Prism' s a =
   forall p f. (Choice p, Applicative f) => p a (f a) -> p s (f s)
 
 prism' :: forall s a. (a -> s) -> (s -> Maybe a) -> Prism' s a
-prism' to from p = dimap from' to' $ right' p
+prism' inj prj p = dimap prj' inj' $ right' p
   where
-    to' :: Applicative f => Either s (f a) -> f s
-    to' = either pure (fmap to)
+    inj' :: Applicative f => Either s (f a) -> f s
+    inj' = either pure (fmap inj)
 
-    from' :: s -> Either s a
-    from' s = maybe (Left s) Right $ from s
+    prj' :: s -> Either s a
+    prj' s = maybe (Left s) Right $ prj s
 {-# INLINE prism' #-}
 
 preview :: Prism' s a -> s -> Maybe a
@@ -58,3 +70,28 @@
 {-# INLINE (^?) #-}
 
 infixl 8 ^?
+
+type Traversal' s a = forall f. Applicative f => (a -> f a) -> s -> f s
+
+ix :: Int -> Traversal' [a] a
+ix 0 f (x : xs) = (: xs) <$> f x
+ix n f (x : xs) = (x :) <$> ix (n - 1) f xs
+ix _ _ [] = pure []
+
+type Iso' s a = forall p f. (Profunctor p, Functor f) => p a (f a) -> p s (f s)
+
+type AnIso' s a = Exchange a a a (Identity a) -> Exchange a a s (Identity s)
+
+data Exchange a b s t = Exchange (s -> a) (b -> t)
+
+instance Profunctor (Exchange a b) where
+  dimap f g (Exchange sa bt) = Exchange (sa . f) (g . bt)
+
+iso :: (s -> a) -> (a -> s) -> Iso' s a
+iso f t = dimap f (fmap t)
+{-# INLINE iso #-}
+
+from :: AnIso' s a -> Iso' a s
+from l = iso (runIdentity . t) f
+  where
+    Exchange f t = l $ Exchange id Identity
diff --git a/src/Network/AWS/ARN/Lambda.hs b/src/Network/AWS/ARN/Lambda.hs
--- a/src/Network/AWS/ARN/Lambda.hs
+++ b/src/Network/AWS/ARN/Lambda.hs
@@ -28,6 +28,10 @@
 import GHC.Generics (Generic)
 import Network.AWS.ARN.Internal.Lens (Prism', prism')
 
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Network.AWS.ARN.Internal.Lens ((^?))
+
 -- | An AWS Lambda function name, and optional alias/version qualifier.
 --
 -- >>> "function:helloworld" ^? _Function
diff --git a/test/Network/AWS/ARN/Test.hs b/test/Network/AWS/ARN/Test.hs
--- a/test/Network/AWS/ARN/Test.hs
+++ b/test/Network/AWS/ARN/Test.hs
@@ -4,7 +4,6 @@
 module Network.AWS.ARN.Test where
 
 import Data.List.NonEmpty (NonEmpty (..))
-import qualified Data.List.NonEmpty as NonEmpty
 import Data.Text (Text)
 import Network.AWS.ARN
 import Network.AWS.ARN.Internal.Lens (Lens', over, preview, review)
@@ -33,10 +32,7 @@
             (review _ARN <$> preview _ARN authorizerSampleARN)
               @?= Just authorizerSampleARN,
           testCase "edit path of Lambda Authorizer ARN" $
-            over
-              (_ARN . arnResource . slashes)
-              (\parts -> prependList (NonEmpty.take 2 parts) ("*" :| []))
-              authorizerSampleARN
+            over (_ARN . arnResource . slashes) (\parts -> take 2 parts ++ ["*"]) authorizerSampleARN
               @?= "arn:aws:execute-api:us-east-1:123456789012:my-spiffy-api/stage/*"
         ]
     ]
