base64-lens: Optics for the Base64 library

[ bsd3, data, library ] [ Propose Tags ] [ Report a vulnerability ]

Optics and pattern synonyms for Base64 values


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

Versions [RSS] 0.1.0.0, 0.1.0.1, 0.1.0.3, 0.2.0, 0.3.0, 0.3.1, 0.4
Change log CHANGELOG.md
Dependencies base (>=4.18 && <4.23), base64 (>=2.0 && <2.1), bytestring (>=0.11 && <0.13), lens (>=5.0 && <5.4), text (>=2.0 && <2.3), text-short (>=0.1 && <0.2) [details]
Tested with ghc ==9.6.7 || ==9.8.4 || ==9.10.3 || ==9.12.4 || ==9.14.1
License BSD-3-Clause
Copyright (c) 2019-2026 Emily Pillmore
Author Emily Pillmore
Maintainer Emily Pillmore <emilypi@cohomolo.gy>
Uploaded by topos at 2026-10-14T04:02:57Z
Category Data
Home page https://github.com/emilypi/base64-lens
Bug tracker https://github.com/emilypi/base64-lens/issues
Source repo head: git clone https://github.com/emilypi/base64-lens.git
Distributions
Downloads 2347 total (23 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-10-14 [all 1 reports]

Readme for base64-lens-0.4

[back to package description]

Base64-lens

Build Status Hackage

Optics and bidirectional patterns for base64, covering strict, lazy, and short ByteString and Text values. Version 0.4 supports base64 2.0, lens 5.0–5.3, and GHC 9.6–9.14.

Encoding and decoding

The string modules expose typed isomorphisms. Viewing decodes a Base64 value; reviewing encodes the payload and returns a wrapper with the matching alphabet.

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}

import Control.Lens
import Data.Base64.Types (Alphabet(..), Base64)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Base64.Lens as B64

encoded :: Base64 'StdPadded ByteString
encoded = B64._Base64 # "Sun"

-- "Sun"
decoded = encoded ^. B64._Base64

-- Right "Sun": validate external input before using the typed optic.
checked = fmap (view B64._Base64) (Base64.parseBase64 "U3Vu")

Each string module provides these optics, where a is its string type:

Optic Type
_Base64 Iso' (Base64 'StdPadded a) a
_Base64Url Iso' (Base64 'UrlPadded a) a
_Base64UrlUnpadded Iso' (Base64 'UrlUnpadded a) a
_Base64Lenient Iso' (Base64 'NonStandard a) a
_Base64UrlLenient Iso' (Base64 'NonStandard a) a

The corresponding patterns construct encoded wrappers and match decoded payloads. For example, B64.Base64 "Sun" has type Base64 'StdPadded ByteString, and matching B64.Base64 bytes binds the decoded bytes. Each pattern is complete for its input type.

The lenient optics ignore invalid Base64 characters, stop at padding, and construct canonical padded output tagged as NonStandard. They are not lawful isomorphisms for arbitrary input.

The Text optics expect the decoded payload to be UTF-8, as in the underlying library. Invalid UTF-8 may throw an exception. Use the ByteString optics for arbitrary binary payloads.

Untyped decoding prisms

For ordinary encoded strings, each string module also provides:

Prism Checked decoder Review produces
_Base64Untyped decodeBase64Untyped Standard, padded Base64
_Base64UrlUntyped URL decodeBase64Untyped URL-safe, padded Base64
_Base64UrlUnpaddedUntyped URL decodeBase64UnpaddedUntyped URL-safe, unpadded Base64

Each has type Prism' a a, where a is the module's string type. Preview returns the decoded payload on success and Nothing on a Base64 decoding error; review encodes directly to an ordinary string.

-- Just "Sun"
untypedDecoded = "U3Vu" ^? B64._Base64Untyped

-- Nothing: non-canonical padding bits
untypedRejected = "U3V=" ^? B64._Base64Untyped

-- "U3Vu"
untypedEncoded = B64._Base64Untyped # "Sun"

The URL prism accepts both padded and unpadded input, matching its decoder. Reviewing a successful match adds any required padding, so it is not a lawful prism over all accepted input. The unpadded URL prism rejects padding. The Text prisms retain the underlying decoders' UTF-8 requirement described above.

Wrapper and alphabet optics

Data.Base64.Types.Lens gives the wrapper its own namespace. Its _Base64 accesses the encoded payload and can change its representation while preserving the alphabet index:

import qualified Data.Base64.Types.Lens as Wrapped
import qualified Data.ByteString.Lazy as BL

-- "U3Vu": the encoded payload
payload = encoded ^. Wrapped._Base64

lazyEncoded :: Base64 'StdPadded BL.ByteString
lazyEncoded = encoded & Wrapped._Base64 %~ BL.fromStrict

Importing this module also enables the standard Wrapped and Rewrapped instances, so _Wrapped and _Wrapped' work with Base64. Wrapping uses assertBase64: it does not encode or validate. Payload changes must preserve the claimed encoding if the result will be decoded. These optics keep the alphabet index fixed; they do not silently change standard Base64 into URL-safe Base64.

The same module supplies _StdPadded, _UrlPadded, _UrlUnpadded, and _NonStandard, each a Prism' Alphabet ().

Data.Text.Encoding.Base64.Error.Lens provides _DecodeError and a type-changing _ConversionError prism for mapping conversion failures.

Migrating from 0.3

The existing optic names now operate on Base64 wrappers. Use view or (^.) for typed decoding. Use parseBase64 to validate external standard-alphabet input, then decode the returned wrapper. Extract encoded output with Wrapped._Base64 or extractBase64 when an API expects an ordinary string.

To retain raw-string prism usage, switch _Base64 to _Base64Untyped, _Base64Url to _Base64UrlUntyped, and _Base64UrlUnpadded to _Base64UrlUnpaddedUntyped. These support preview or (^?) as before.

The lenient patterns also change direction: construction encodes and matching decodes, consistently with the other patterns.

Development

cabal update
cabal test --test-show-details=direct
cabal haddock
cabal check

To exercise the optional SIMD backend in base64:

cabal test --constraint='base64 +simd' --test-show-details=direct