base-compat

A compatibility layer for base

Version on this page:0.10.4
LTS Haskell 22.14:0.13.1
Stackage Nightly 2024-03-28:0.13.1
Latest on Hackage:0.13.1

See all snapshots base-compat appears in

MIT licensed and maintained by Simon Hengel, João Cristóvão, Ryan Scott
This version can be pinned in stack with:base-compat-0.10.4@sha256:f3795c7048c104443600334c9ffa2f7cabc78cf45dc9cd6342d46c37facb9258,7181

Module documentation for 0.10.4

  • Control
    • Control.Concurrent
      • Control.Concurrent.Compat
        • Control.Concurrent.Compat.Repl
      • Control.Concurrent.MVar
        • Control.Concurrent.MVar.Compat
          • Control.Concurrent.MVar.Compat.Repl
    • Control.Exception
      • Control.Exception.Compat
        • Control.Exception.Compat.Repl
    • Control.Monad
      • Control.Monad.Compat
        • Control.Monad.Compat.Repl
      • Control.Monad.Fail
        • Control.Monad.Fail.Compat
          • Control.Monad.Fail.Compat.Repl
      • Control.Monad.IO
        • Control.Monad.IO.Class
          • Control.Monad.IO.Class.Compat
            • Control.Monad.IO.Class.Compat.Repl
      • Control.Monad.ST
        • Control.Monad.ST.Lazy
          • Control.Monad.ST.Lazy.Unsafe
            • Control.Monad.ST.Lazy.Unsafe.Compat
              • Control.Monad.ST.Lazy.Unsafe.Compat.Repl
        • Control.Monad.ST.Unsafe
          • Control.Monad.ST.Unsafe.Compat
            • Control.Monad.ST.Unsafe.Compat.Repl
  • Data
    • Data.Bifoldable
      • Data.Bifoldable.Compat
        • Data.Bifoldable.Compat.Repl
    • Data.Bifunctor
      • Data.Bifunctor.Compat
        • Data.Bifunctor.Compat.Repl
    • Data.Bitraversable
      • Data.Bitraversable.Compat
        • Data.Bitraversable.Compat.Repl
    • Data.Bits
      • Data.Bits.Compat
        • Data.Bits.Compat.Repl
    • Data.Bool
      • Data.Bool.Compat
        • Data.Bool.Compat.Repl
    • Data.Complex
      • Data.Complex.Compat
        • Data.Complex.Compat.Repl
    • Data.Either
      • Data.Either.Compat
        • Data.Either.Compat.Repl
    • Data.Foldable
      • Data.Foldable.Compat
        • Data.Foldable.Compat.Repl
    • Data.Function
      • Data.Function.Compat
        • Data.Function.Compat.Repl
    • Data.Functor
      • Data.Functor.Compat
        • Data.Functor.Compat.Repl
      • Data.Functor.Compose
        • Data.Functor.Compose.Compat
          • Data.Functor.Compose.Compat.Repl
      • Data.Functor.Const
        • Data.Functor.Const.Compat
          • Data.Functor.Const.Compat.Repl
      • Data.Functor.Contravariant
        • Data.Functor.Contravariant.Compat
          • Data.Functor.Contravariant.Compat.Repl
      • Data.Functor.Identity
        • Data.Functor.Identity.Compat
          • Data.Functor.Identity.Compat.Repl
      • Data.Functor.Product
        • Data.Functor.Product.Compat
          • Data.Functor.Product.Compat.Repl
      • Data.Functor.Sum
        • Data.Functor.Sum.Compat
          • Data.Functor.Sum.Compat.Repl
    • Data.IORef
      • Data.IORef.Compat
        • Data.IORef.Compat.Repl
    • Data.List
      • Data.List.Compat
        • Data.List.Compat.Repl
      • Data.List.NonEmpty
        • Data.List.NonEmpty.Compat
          • Data.List.NonEmpty.Compat.Repl
    • Data.Monoid
      • Data.Monoid.Compat
        • Data.Monoid.Compat.Repl
    • Data.Proxy
      • Data.Proxy.Compat
        • Data.Proxy.Compat.Repl
    • Data.Ratio
      • Data.Ratio.Compat
        • Data.Ratio.Compat.Repl
    • Data.STRef
      • Data.STRef.Compat
        • Data.STRef.Compat.Repl
    • Data.Semigroup
      • Data.Semigroup.Compat
        • Data.Semigroup.Compat.Repl
    • Data.String
      • Data.String.Compat
        • Data.String.Compat.Repl
    • Data.Type
      • Data.Type.Coercion
        • Data.Type.Coercion.Compat
          • Data.Type.Coercion.Compat.Repl
    • Data.Version
      • Data.Version.Compat
        • Data.Version.Compat.Repl
    • Data.Void
      • Data.Void.Compat
        • Data.Void.Compat.Repl
    • Data.Word
      • Data.Word.Compat
        • Data.Word.Compat.Repl
  • Debug
    • Debug.Trace
      • Debug.Trace.Compat
        • Debug.Trace.Compat.Repl
  • Foreign
    • Foreign.Compat
      • Foreign.Compat.Repl
    • Foreign.ForeignPtr
      • Foreign.ForeignPtr.Compat
        • Foreign.ForeignPtr.Compat.Repl
      • Foreign.ForeignPtr.Safe
        • Foreign.ForeignPtr.Safe.Compat
          • Foreign.ForeignPtr.Safe.Compat.Repl
      • Foreign.ForeignPtr.Unsafe
        • Foreign.ForeignPtr.Unsafe.Compat
          • Foreign.ForeignPtr.Unsafe.Compat.Repl
    • Foreign.Marshal
      • Foreign.Marshal.Alloc
        • Foreign.Marshal.Alloc.Compat
          • Foreign.Marshal.Alloc.Compat.Repl
      • Foreign.Marshal.Array
        • Foreign.Marshal.Array.Compat
          • Foreign.Marshal.Array.Compat.Repl
      • Foreign.Marshal.Compat
        • Foreign.Marshal.Compat.Repl
      • Foreign.Marshal.Safe
        • Foreign.Marshal.Safe.Compat
          • Foreign.Marshal.Safe.Compat.Repl
      • Foreign.Marshal.Unsafe
        • Foreign.Marshal.Unsafe.Compat
          • Foreign.Marshal.Unsafe.Compat.Repl
      • Foreign.Marshal.Utils
        • Foreign.Marshal.Utils.Compat
          • Foreign.Marshal.Utils.Compat.Repl
  • Numeric
    • Numeric.Compat
      • Numeric.Compat.Repl
    • Numeric.Natural
      • Numeric.Natural.Compat
        • Numeric.Natural.Compat.Repl
  • Prelude
    • Prelude.Compat
      • Prelude.Compat.Repl
  • System
    • System.Environment
      • System.Environment.Compat
        • System.Environment.Compat.Repl
    • System.Exit
      • System.Exit.Compat
        • System.Exit.Compat.Repl
    • System.IO
      • System.IO.Unsafe
        • System.IO.Unsafe.Compat
          • System.IO.Unsafe.Compat.Repl
  • Text
    • Text.Read
      • Text.Read.Compat
        • Text.Read.Compat.Repl
  • Type
    • Type.Reflection
      • Type.Reflection.Compat
        • Type.Reflection.Compat.Repl

A compatibility layer for base

Hackage Hackage Dependencies Haskell Programming Language BSD3 License

Scope

The scope of base-compat is to provide functions available in later versions of base to a wider (older) range of compilers.

In addition, successful library proposals that have been accepted to be part of upcoming versions of base are also included. This package is not intended to replace base, but to complement it.

Note that base-compat does not add any orphan instances. There is a separate package base-orphans for that.

In addition, base-compat only backports functions. In particular, we purposefully do not backport data types or type classes introduced in newer versions of base. For more info, see the Data types and type classes section.

base-compat is intentionally designed to have zero dependencies. As a consequence, there are some modules that can only be backported up to certain versions of base. If an even wider support window is desired in these scenarios, there also exists a base-compat-batteries package which augments base-compat with certain compatibility package dependencies. For more info, see the Dependencies section.

Basic usage

In your cabal file, you should have something like this:

  build-depends:      base              >= 4.3
                    , base-compat       >= 0.9.0

Then, lets say you want to use the isRight function introduced with base-4.7.0.0. Replace:

import Data.Either

with

import Data.Either.Compat

Note (1): There is no need to import both unqualified. The .Compat modules re-exports the original module.

Note (2): If a given module .Compat version is not defined, that either means that:

  • The module has not changed in recent base versions, thus no .Compat is needed.
  • The module has changed, but the changes depend on newer versions of GHC, and thus are not portable.
  • The module has changed, but those changes have not yet been merged in base-compat: patches are welcomed!

Using Prelude.Compat

If you want to use Prelude.Compat (which provides all the AMP/Traversable/Foldable changes from base-4.8.0.0), it’s best to hide Prelude, e.g.:

import Prelude ()
import Prelude.Compat

main :: IO ()
main = mapM_ print (Just 23)

Alternatively, you can use the NoImplicitPrelude language extension:

{-# LANGUAGE NoImplicitPrelude #-}
import Prelude.Compat

main :: IO ()
main = mapM_ print (Just 23)

Note that we use

mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()

from Data.Foldable here, which is only exposed from Prelude since base-4.8.0.0.

Using this approach allows you to write code that works seamlessly with all versions of GHC that are supported by base-compat.

What is covered

So far the following is covered.

For compatibility with the latest released version of base

  • Prelude.Compat incorporates the AMP/Foldable/Traversable changes and exposes the same interface as Prelude from base-4.9.0.0
  • System.IO.Error.catch is not re-exported from Prelude.Compat for older versions of base
  • Text.Read.Compat.readMaybe
  • Text.Read.Compat.readEither
  • Data.Monoid.Compat.<>
  • Added bitDefault, testBitDefault, and popCountDefault to Data.Bits.Compat
  • Added toIntegralSized to Data.Bits.Compat (if using base-4.7)
  • Added bool function to Data.Bool.Compat
  • Added isLeft, isRight, fromLeft, and fromRight to Data.Either.Compat
  • Added forkFinally to Control.Concurrent.Compat
  • Added withMVarMasked function to Control.Concurrent.MVar.Compat
  • Added (<$!>) function to Control.Monad.Compat
  • Weakened RealFloat constraints on realPart, imagPart, conjugate, mkPolar, and cis in Data.Complex.Compat
  • Added more efficient maximumBy/minimumBy to Data.Foldable.Compat
  • Added ($>) and void functions to Data.Functor.Compat
  • (&) function to Data.Function.Compat
  • ($>) and void functions to Data.Functor.Compat
  • modifyIORef', atomicModifyIORef' and atomicWriteIORef to Data.IORef.Compat
  • dropWhileEnd, isSubsequenceOf, sortOn, and uncons functions to Data.List.Compat
  • Correct versions of nub, nubBy, union, and unionBy to Data.List.Compat
  • asProxyTypeOf with a generalized type signature to Data.Proxy.Compat
  • modifySTRef' to Data.STRef.Compat
  • String, lines, words, unlines, and unwords to Data.String.Compat
  • gcoerceWith to Data.Type.Coercion.Compat
  • makeVersion function to Data.Version.Compat
  • traceId, traceShowId, traceM, and traceShowM functions to Debug.Trace.Compat
  • byteSwap16, byteSwap32, and byteSwap64 to Data.Word.Compat
  • plusForeignPtr to Foreign.ForeignPtr.Compat
  • calloc and callocBytes functions to Foreign.Marshal.Alloc.Compat
  • callocArray and callocArray0 functions to Foreign.Marshal.Array.Compat
  • fillBytes to Foreign.Marshal.Utils.Compat
  • Added Data.List.Compat.scanl'
  • showFFloatAlt and showGFloatAlt to Numeric.Compat
  • lookupEnv, setEnv and unsetEnv to System.Environment.Compat
  • unsafeFixIO and unsafeDupablePerformIO to System.IO.Unsafe.IO
  • RuntimeRep-polymorphic ($!) to Prelude.Compat
  • RuntimeRep-polymorphic throw to Control.Exception.Compat

What is not covered

Data types and type classes

base-compat purposefully does not export any data types or type classes that were introduced in more recent versions of base. The main reasoning for this policy is that it is not some data types and type classes have had their APIs change in different versions of base, which makes having a consistent compatibility API for them practically impossible.

As an example, consider the FiniteBits type class. It was introduced in base-4.7.0.0 with the following API:

class Bits b => FiniteBits b where
    finiteBitSize :: b -> Int

However, in base-4.8.0.0, FiniteBits gained additional functions:

class Bits b => FiniteBits b where
    finiteBitSize :: b -> Int
    countLeadingZeros :: b -> Int   -- ^ @since 4.8.0.0
    countTrailingZeros :: b -> Int  -- ^ @since 4.8.0.0

This raises the question: how can FiniteBits be backported consistently across all versions of base? One approach is to backport the API exposed in base-4.8.0.0 on versions prior to 4.7.0.0. The problem with this is that countLeadingZeros and countTrailingZeros are not exposed in base-4.7.0.0, so instances of FiniteBits would have to be declared like this:

instance FiniteBits Foo where
    finiteBitSize = ...
#if MIN_VERSION_base(4,8,0) || !(MIN_VERSION_base(4,7,0))
    countLeadingZeros = ...
    countTrailingZeros = ...
#endif

Another approach is to backport the API from base-4.7.0.0 and to declare additional methods outside of the class:

#if MIN_VERSION_base(4,7,0) && !(MIN_VERSION_base(4,8,0))
countLeadingZeros :: FiniteBits b => b -> Int
countLeadingZeros = {- default implementation #-}
#endif

The situation is only slightly better for classes which exist across all versions of base, but have grown their API. For example, it’s tempting to define

#if !(MIN_VERSION_base(4,8,0))
displayException :: Exception e => e -> String
displayException = show
#endif

As with the previous approach, you won’t be able to define new members of the type class without CPP guards. In other words, the non-CPP approach would limit uses to the lowest common denominator.

As neither approach is a very satisfactory solution, and to embrace consistency, we do not pursue either approach. For similar reasons, we do not backport data types.

Dependencies

base-compat is designed to have zero dependencies (besides libraries that ship with GHC itself). A consequence of this choice is that there are certain modules that have a “limited” support window. An important example of this is Prelude.Compat, which backports the Semigroup class to versions of base older than 4.11 (when it was added to the Prelude). Because Semigroup was not added to base until base-4.9, base-compat cannot backport it to any earlier version of base than this.

If you would instead desire to be able to use a version of Prelude.Compat that does backport Semigroup to even older versions of base, even if it means pulling in other dependencies, then you are in luck. There also exists a base-compat-batteries package, which exposes a strict superset of the API in base-compat. base-compat-batteries has all the same modules as base-compat, but exposes more functionality on more versions of base by reexporting things from compatibility libraries whenever necessary. (For instance, base-compat-batteries exports the Semigroup class from the semigroups library when built against versions of base older than 4.9.)

Because base-compat and base-compat-batteries have the same module names, they are quite easy to switch out for one another in library projects, at the expense of having clashing names if one tries to import them in GHCi. To work around this issue, base-compat and base-compat-batteries also provide copies of each module with the suffix .Repl (for base-compat) and .Repl.Batteries (for base-compat-batteries) to give them globally unique namespaces in the event one wants to import them into GHCi.

Here is a list of compatibility libraries that base-compat-batteries depends on, paired with the things that each library backports:

Supported versions of GHC/base

  • ghc-8.6.1 / base-4.12.0.0
  • ghc-8.4.3 / base-4.11.1.0
  • ghc-8.4.2 / base-4.11.1.0
  • ghc-8.4.1 / base-4.11.0.0
  • ghc-8.2.2 / base-4.10.1.0
  • ghc-8.2.1 / base-4.10.0.0
  • ghc-8.0.2 / base-4.9.1.0
  • ghc-8.0.1 / base-4.9.0.0
  • ghc-7.10.3 / base-4.8.2.0
  • ghc-7.10.2 / base-4.8.1.0
  • ghc-7.10.1 / base-4.8.0.0
  • ghc-7.8.4 / base-4.7.0.2
  • ghc-7.8.3 / base-4.7.0.1
  • ghc-7.8.2 / base-4.7.0.0
  • ghc-7.8.1 / base-4.7.0.0
  • ghc-7.6.3 / base-4.6.0.1
  • ghc-7.6.2 / base-4.6.0.1
  • ghc-7.6.1 / base-4.6.0.0
  • ghc-7.4.2 / base-4.5.1.0
  • ghc-7.4.1 / base-4.5.0.0
  • ghc-7.2.2 / base-4.4.1.0
  • ghc-7.2.1 / base-4.4.0.0
  • ghc-7.0.4 / base-4.3.1.0
  • ghc-7.0.3 / base-4.3.1.0
  • ghc-7.0.2 / base-4.3.1.0
  • ghc-7.0.1 / base-4.3.0.0

We also make an attempt to keep base-compat building with GHC HEAD, but due to its volatility, it may not work at any given point in time. If it doesn’t, please report it!

Patches are welcome; add tests for new code!

Changes

Changes in 0.10.4 [2018.07.03]

  • Make more modules Trustworthy. In particular, fix a regression in which Prelude.Compat was inferred as Unsafe by explicitly marking it as Trustwothy.

Changes in 0.10.3 [2018.07.02]

  • Backport the proper fixity for ($!), which was accidentally omitted in base-compat-0.10.2.

Changes in 0.10.2 [2018.07.02]

  • Sync with base-4.12/GHC 8.6

  • Backport RuntimeRep-polymorphic versions of ($!) and throw to Prelude.Compat and Control.Exception.Compat, respectively (if using base-4.10/GHC 8.2 or later).

  • Introduce the Data.Functor.Contravariant.Compat module, which reexports Data.Functor.Contravariant if using base-4.12/GHC 8.6 or later.

    See Data.Functor.Contravariant.Compat in the corresponding base-compat-batteries release for a version with a wider support window.

Changes in 0.10.1 [2018.04.10]

  • Add Data.List.NonEmpty.Compat.
  • Reexport (Data.Semigroup.<>) from Data.Monoid.Compat back to base-4.9.

Changes in 0.10.0 [2018.04.05]

  • Sync with base-4.11/GHC 8.4

  • Backport Semigroup((<>)) to Prelude.Compat.

    Note that the Semigroup class has only been in base since base-4.9/GHC 8.0, so accordingly, this can only be backported back to GHC 8.0. If you wish to have a version of Prelude.Compat that backports Semigroup to older GHCs (by conditionally depending on the semigroups library), use the Prelude.Compat module from the base-compat-batteries package.

  • Backport (<&>) to Data.Functor.Compat

  • Backport iterate' to Data.List.Compat

  • Backport showHFloat to Numeric.Compat

  • Backport a RuntimeRep-polymorphic withTypeable function to Type.Reflection.Compat. (This is only exported on base-4.10/GHC 8.2.)

  • Introduce the following modules, back until the oldest version of base that can support backporting them. If you wish to use them in conjunction with older versions of base, use the base-compat-batteries package.

    • Control.Monad.Fail.Compat (back until base-4.9/GHC 8.0)
    • Control.Monad.IO.Class.Compat (back until base-4.9/GHC 8.0)
    • Data.Bifunctor (back until base-4.8/GHC 7.10)
    • Data.Bifoldable and Data.Bitraversable (back until base-4.10/GHC 8.2)
    • Data.Functor.Compose.Compat, Data.Functor.Product.Compat, and Data.Functor.Sum.Compat (back until base-4.9/GHC 8.0)
    • Data.Functor.Identity.Compat (back until base-4.8/GHC 7.10)
    • Data.Semigroup.Compat (back until base-4.9/GHC 8.0)
    • Data.Void.Compat (back until base-4.8/GHC 7.10)
    • Numeric.Natural.Compat (back until base-4.8/GHC 7.10)
  • Introduce versions of modules with the suffix .Repl. These simply reexport the contents of their counterparts without the .Repl suffix to provide a globally unique namespace to import from in the event one wants to import base-compat modules into GHCi. (In base-compat-batteries, the corresponding suffix is .Repl.Batteries.)

Changes in 0.9.3 [2017.04.10]

  • Sync with base-4.10/GHC 8.2
  • Backport fromLeft/fromRight to Data.Either.Compat
  • Backport implementations of maximumBy/minimumBy which use constant stack space to Data.Foldable.Compat
  • Backport asProxyTypeOf with a generalized type signature to Data.Proxy.Compat
  • Backport gcoerceWith to Data.Type.Coercion.Compat
  • Backport plusForeignPtr to Foreign.ForeignPtr.Compat

Changes in 0.9.2

  • Allow building on the HaLVM

Changes in 0.9.1

  • Use the more efficient version of replicateM and replicateM_ introduced in base-4.9

Changes in 0.9.0

  • Sync with base-4.9/GHC 8.0
  • Weakened RealFloat constraints on realPart, imagPart, conjugate, mkPolar, and cis in Data.Complex.Compat
  • Backport Foreign.ForeignPtr.Safe and Foreign.Marshal.Safe
  • Generalize filterM, forever, mapAndUnzipM, zipWithM, zipWithM_, replicateM, and replicateM_ in Control.Monad from Monad to Applicative
  • Backport .Unsafe.Compat modules (for Control.Monad.ST, Control.Monad.ST.Lazy, Foreign.ForeignPtr, and Foreign.Marshal)
  • Backport forkFinally and forkOSWithUnmask to Control.Concurrent.Compat
  • Backport Data.Functor.Const
  • Backport modifyIORef', atomicModifyIORef' and atomicWriteIORef to Data.IORef.Compat
  • Data.Ratio.{denominator,numerator} have no Integral constraint anymore
  • Backport modifySTRef' to Data.STRef.Compat
  • Export String, lines, words, unlines, and unwords to Data.String.Compat
  • Generalize Debug.Trace.{traceM, traceShowM} from Monad to Applicative
  • Backport errorWithoutStackTrace to Prelude.Compat
  • Backport unsafeFixIO and unsafeDupablePerformIO to System.IO.Unsafe.Compat

Changes in 0.8.2

  • Backport bitDefault, testBitDefault, and popCountDefault in Data.Bits.Compat to all versions of base
    • Backport toIntegralSized to base-4.7
  • Backport nub and nubBy (as well as union and unionBy, which are implemented in terms of them) to fix logic error in Data.List.Compat
  • Backport byteSwap16, byteSwap32, and byteSwap64 to Data.Word.Compat
  • Backport fillBytes in Foreign.Marshal.Utils.Compat
  • Backport showFFloatAlt and showGFloatAlt to Numeric.Compat

Changes in 0.8.1.1

  • Fixed Windows build

Changes in 0.8.1

  • Implement setEnv and unsetEnv in System.Environment.Compat (which were ported from the setenv package). As a result, base-compat now depends on unix on POSIX-like operating systems.
  • Drop GHC 6.12 (and base-4.2.0.0) compatibility

Changes in 0.8.0.1

  • Retrospective version bump updating the changelog to reflect the changes made in 0.8.0

Changes 0.8.0

  • All orphan instances were split off into a separate package, base-orphans
  • base-compat no longer redefines the data types Down and Alt. See here for the discussion that led to this change.
  • Update Control.Monad.Compat for base-4.8.0.0
  • Update Data.List.Compat for base-4.8.0.0
  • Update Data.Foldable.Compat for base-4.8.0.0

Changes in 0.7.1

  • Backported Alt to Data.Monoid.Compat
  • Backported Down to Data.Ord.Compat

Changes in 0.7.0

  • Add functions and orphan instances introduced by changes to base-4.7.0.0 and base-4.8.0.0

Changes in 0.6.0

  • Update Prelude.Compat for base-4.8.0.0 and AMP

Changes in 0.5.0

  • Remove Control.Exception.Base.Compat and GHC.Exception.Compat
  • Add System.Exit.Compat.die
  • Compatibility with base-4.7.1

Changes in 0.4.1

  • Add setEnv and unsetEnv to System.Environment.Compat

Changes in 0.4.0

  • Major refactoring: base-compat no longer aims to replace all base, only new code is included in module .Compat
  • Removed stubbed modules
  • Removed generation scripts

Changes in 0.3

  • Added functions from Base 4.7 (bool, isLeft, isRight)
  • Added instances from Base 4.7 (Either Foldable, Traversable,…)

Changes in 0.2.1

  • Fix build on windows

Changes in 0.2.0

  • Re-export everything from base
  • provides access to VERSION_base and MIN_VERSION_base CPP macros (with #include "base-compat.h")
  • Do not re-export System.IO.Error.catch from Prelude for base < 4.6.0
  • Add Eq/Ord instance for ErrorCall
  • Remove GHC.IOBase, GHC.Handle, Control.Concurrent.QSem, Control.Concurrent.QSemN, Control.Concurrent.SampleVar, Data.HashTable

Changes in 0.1.0

  • Remove getExecutablePath, it did not work with GHC < 7.2 (patches welcome!)
  • Add <>