marvin-interpolate: Compile time string interpolation a la Scala and CoffeeScript

[ bsd3, library, text ] [ Propose Tags ]

The official documentation can be found on readthedocs

[Skip to Readme]
Versions [faq] 0.0.1, 0.1.0, 0.2.0, 0.3.0, 0.4.0, 1.0, 1.1, 1.1.1, 1.1.2
Dependencies base (>=4.7 && <5), haskell-src-meta (>= && <0.7), mtl (>=2.2.1 && <2.3), parsec (>=3.1.11 && <3.2), template-haskell (>= && <2.12), text (>= && <1.3) [details]
License BSD-3-Clause
Copyright Copyright: (c) 2016 Justus Adam
Author JustusAdam
Category Text
Home page
Source repo head: git clone
Uploaded by justus at Sun Jan 1 12:49:20 UTC 2017
Distributions NixOS:1.1.2
Downloads 2696 total (60 in the last 30 days)
Rating (no votes yet) [estimated by rule of succession]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2017-01-01 [all 1 reports]




Maintainer's Corner

For package maintainers and hackage trustees

Readme for marvin-interpolate-0.1.0

[back to package description]

Simple string interpolation

Travis Hackage

This string interpolation library originates from the Marvin project where, in an attempt to make it easy for the user to write text with some generated data in it, I developed this string interpolation library. The design is very similar to the string interpolation in Scala and CoffeeScript, in that the hard work happens at compile time (no parsing overhead at runtime) and any valid Haskell expression can be interpolated.

The library uses the builtin Haskell compiler extension in the form of QuasiQuoters (QuasiQuotes language extension) and/or splices (Template Haskell language extension)

{-# LANGUAGE QuasiQuotes #-}

import Marvin.Interpolate

myStr = [i|some string %{show $ map succ [1,2,3]} and data |]
-- "some string [2,3,4] and data"

or alternatively as splice (which might be kinder to your code highlighting)

{-# LANGUAGE TemplateHaskell #-}

import Marvin.Interpolate

myStr = $(is "some string %{show $ map succ [1,2,3]} and data")
-- "some string [2,3,4] and data"

It basically transforms the interpolated string [i|interpolated string|], or in splices $(is "interpolated string") into a concatenation of all string bits and the expressions in %{}. Therefore it is not limited to String alone, rather it produces a literal at compile time, which can either be interpreted as String or, using the OverloadedStrings extension, as Text or ByteString or any other string type.

i (for interpolate) and is (for interpolate splice) is the basic interpolator, which inserts the expressions verbatim. Hence when using i or is all expressions must return the desired string type.

There are specialized interpolators, which also perform automatic conversion of non-string types into the desired string type. These specialized interpolators each have an associated typeclass, which converts string types (String, Text and lazy Text) to the target type, but leaves the contents unchanged and calls show on all other types before converting. This last instance, which is based on Show, can be overlapped by specifying a custom instance for your type, allowing the user to define the conversion.

The naming scheme of the interpolators in general is i<splice?><pecialization?>. I. e. isS expands to interpolate splice to String and iLT to interpolate to Lazy Text.

  • iS and isS in Marvin.Interpolate.String converts to String via the ShowS typeclass
  • iT and isT in Marvin.Interpolate.Text converts to Text via the ShowT typeclass
  • iLT and isLT in Marvin.Interpolate.Text.Lazy converts to lazy Text via the ShowLT typeclass

To import all interpolators, import Marvin.Interpolate.All.


Interpolation uses the quasi quoter sytax, which starts with [interpolator_name| and ends with |]. Anything in between is interpreted by the library.

The format string in between uses the syntax %{expression}. Any valid Haskell expression can be used inside the braces. And all names which are in scope can be used, like so.

let x = 5 in [iS|x equals %{x}|] -- > "x equals 5"

There are four escape sequences to allow literal %{ and |]

| Input | Output | |-------|--------| | ~] | ] | | ~% | % | | ~} | } | | ~~ | ~ |

As a result the sequence ~%{ will show up as a literal %{ in the output and |~] results in a literal |]. Note that these are simple substitutions. In general the characters themselves, if not escaped, will not throw errors, aka ~ will be ~ again in the output. Escaping is only necessary in cases where these cahracters would have a special meaning otherwise.

Differences to/Advantages over other libraries

There are a few advantages this libary has over other string formatting options.

  1. The hard work happens at compile time

    Unlike libraries like text-format and the Text.Printf module parsing the format string, producing the string fragments and interleaving data and strings happens all at compile time. At runtime a single fusable string concatenation expression is produced.
    Furthermore all errors, like missing identifiers happen at compile time, not at runtime.

  2. Type Polymorphism

    The created, interpolated string has no type. It can be interpreted as any string type, so long as there is an IsString instance and the expressions inside return the appropriate type.

    This is different format string libraries like text-format and the Text.Printf module which always produce strings of a particular type, and interpolation libraries like interpolate and interpol which require instances of Show.

  3. Simple API and full Haskell support

    The interpolated expressions are just plain Haskell expressions, no extra syntax, beyond the interpolation braces %{}. Also all Haskell expressions, including infix expressions, are fully supported.

    This is different from Interpolation which introduces additional syntax and does not fully support infix expressions.