exon: Monoidal Quasiquote Interpolation

[ library, string ] [ Propose Tags ]

See https://hackage.haskell.org/package/exon/docs/Exon.html


[Skip to Readme]
Versions [RSS] [faq] 0.1.0.0, 0.1.1.0, 0.2.0.0, 0.2.0.1
Change log changelog.md
Dependencies base (==4.*), flatparse (>=0.2), 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-10-26T21:32:57Z
Distributions NixOS:0.2.0.0
Downloads 189 total (15 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs uploaded by user
Build status unknown [no reports yet]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

For package maintainers and hackage trustees

Candidates


Readme for exon-0.2.0.1

[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}|]

Customization

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 " >>> ")

Acknowledgments

Inspired by the magnificent string-interpolate.