exon: Monoidal Quasiquote Interpolation

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

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

[Skip to Readme]


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-07-28T21:59:42Z




Maintainer's Corner

For package maintainers and hackage trustees

Readme for exon-

[back to package description]

This Haskell library provides quasiquote string interpolation with customizable concatenation for arbitrary types.

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.


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.