exon: Monoidal Quasiquote Interpolation

[ library, string ] [ Propose Tags ]
This version is deprecated.


Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS],,,,,,,,,,,,,,,,,,,, (info)
Change log changelog.md
Dependencies base (>=4 && <5), flatparse (>=0.2 && <0.3), haskell-src-exts (<1.24), haskell-src-meta (<0.9), relude (>=0.7 && <1.2), template-haskell, text [details]
License BSD-2-Clause-Patent
Copyright 2021 Torsten Schmits
Author Torsten Schmits
Maintainer haskell@tryp.io
Category String
Home page https://github.com/tek/exon#readme
Bug tracker https://github.com/tek/exon/issues
Source repo head: git clone https://github.com/tek/exon
Uploaded by tek at 2021-08-27T18:28:59Z
Distributions NixOS:, Stackage:
Reverse Dependencies 15 direct, 6 indirect [details]
Downloads 835 total (29 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for exon-

[back to package description]

This Haskell library provides quasiquote string interpolation with customizable concatenation for arbitrary types. Visit hackage to read the full API documentation.

The default case uses Monoid and IsString:

import Exon (exon)
import Data.Text (toUpper)

newtype Name =
  Name Text
  deriving newtype (Show, Monoid, IsString)

instance Semigroup Name where
  Name l <> Name r = Name (l <> " | " <> r)

lastName :: Name
lastName = "Fry"

up :: Name -> Name
up (Name name) = Name (toUpper name)

>>> [exon|Philip J. #{up lastName}|]
Name "Philip | J. | FRY"

Individual segments are tokenized at whitespace boundaries, expressions between #{ and } are inserted verbatim.

The default implementation ignores whitespace when concatenating, while it is preserved for String, Text etc.

An instance for String -> String is provided, used by showsPrec for example:

data Record =
  Record {
    number :: Int,
    maybeNumber :: Maybe Int,
    value :: Value

instance Show Record where
  showsPrec d Record {..} =
    showParen (d > 10) $
      [exon|Record #{showsPrec 11 number} #{showsPrec 11 maybeNumber} #{showsPrec 11 value}|]


Concatenation is performed by the class Exon.Exon:

class Exon (tag :: Type) (a :: Type) where
  convertSegment :: Segment a -> Result a

  appendSegment :: Result a -> Segment a -> Result a

  insertWhitespace :: Result a -> String -> Segment a -> Result a

  concatSegments :: NonEmpty (Segment a) -> a

All methods have default implementations. The tag parameter is an arbitrary type that allows the creation of different quoters, with exon using the tag ExonDefault.

In order to get the default quoter to support custom rules for a type, one simply has to write an instance:

import Exon (Exon, ExonDefault, Result)
import qualified Exon as Segment (Segment(..))

instance Exon ExonDefault Name where
  convertSegment = \case
    Segment.String s -> Result (Name s)
    Segment.Expression name -> Result name
    Segment.Whitespace ws -> Result (Name " >>> ")


Inspired by the magnificent string-interpolate.