ltext logo


General-Purpose Text-File Templating Utility

copy icon

Reduce Duplication

λtext is a generic templating tool for any text file. For languages that don't support import-style code-reuse, it's common to just copy/paste. However, after a change is needed, that change must be replicated manually to every file needing it. With λtext, you parameterize the files themselves, and let λtext do the pasting.

lambda icon

Built for Precision

With Lambda Calculus as the underlying engine, λtext features both generality and precision - its Hindley-Milner type system gives us both polymorphism and correctness; generic templates that seamlessly fit.

How It Works

1. Have a Text File

You will first need a text file to parameterize. This can be Html, JavaScript, Haskell, Bash, LaTeX, anything. In our example, we'll use a plaintext file foo:

Woo hoo! I'm a Text File!

2. Create a Parameter

Just like lambda calculus, we create arity with abstraction - the act of creating a parameter. In λtext, we need to declare our parameters on the first line, inside delimiters. Those parameters are later used within the same delimiters. We are free to choose whatever syntax we want - Jade-style delimiters a 'la {{ ... }}, Html-friendly delimiters via comments <!-- -->, etc. Here's an example parametric foo:

$$ x %%
Woo hoo! I'm a Text File!

$$ x %%

3. Use Another File as Input

Now that we have our function/file foo :: Text -> Text, we need some content to plug-in. Here's another file bar:

Here's some content that I /really/
want inside `foo`!

Note the empty first line - we don't want ltext to think some of our words are actually delimiters or parameters. Optionally, we could designate this file as "raw" with the --raw "bar" flag.

4. Apply the Files/Functions with λtext

To use our function/files, we must apply them to one another - application is the other counterpart to lambda calculus. To do this, invoke the ltext executable from a terminal:

$ ltext "foo bar"

Woo hoo! I'm a Text File!

Here's some content that I /really/
want inside `foo`!

The result expression / file text gets pumped out stdout.

Get λtext


Build From Source

  1. Get Stack
  2. stack install ltext

Advanced Usage

There are a few other things to consider when working with λtext - type checking, partial application, and... debugging! *gasp*

Type Checking

To inspect the type of a file or an expression, just use the -t option during invocation:

$ ltext --type "foo"

foo :: Text -> Text

You can read this as "foo takes in Text and returns Text", or "foo is a function from Text to Text". This is useful if you are getting type matching errors, or want to get a description of a file. We can also check the type signature of expressions directly:

$ ltext --type "\x -> x"

a0 -> a0

This is a little technical, but we are creating a polymorphic function \x -> x, which has a type from for all a0 to a0 (you may see these cryptic type variables instead of Text from time to time). This would happen if, for instance, you made an "apply"-like template:

%% f x %%


%% f x %%

If you check the type of apply, you can see it's...

$ ltext --type "apply"

(a1 -> Text) -> a1 -> Text

Meaning that x can be content of any type, so long as it's the same input as f.

Partial Application

Just like lambda calculus, we can partially apply templates to each other. Take our apply template from before - that's a good example to work though. If we apply our foo file from before to apply, we should get a template from Text -> Text - the parameter to foo being unified with the x parameter in apply:

$ ltext --type "apply foo"

Text -> Text

It turns out, we can actually render this partially applied template, but only if we explicitly supply new delimiters for the result:

$ ltext "apply foo" --left "{{" --right "}}"

{{ x }}


Woo hoo! I'm a Text File!

{{ x }}


If you toyed around with the apply function from earlier, you may have seen a daunting error:

$ ltext "\f -> apply f bar"
[Print Error] Can't print textual data while residually inside an expression:
Abs "f" (Concat (Lit ["###"]) (App (Var "f") (Abs "some" (Abs "content"
  (Abs "that" (Abs "I" (Lit ["###"])))))))

This is intended! This makes sure that the templates we render are actually renderable.

In laymen's terms, a template can only be rendered if all Text data that is concatenated (<>) is at the "top level" - if after reducing our expression, we wind up applying text to an unknown function, or applying unknown input to a file, we can't fully substitute the text, and are left with dangling references to files.

We can check the type of these expressions, but we can't render them. This process will improve in the future, but for now just make sure `ltext` is used to concatenate files completely, and not expect partial application to "just work".

So, if you ever get this error, you know that there's something you still need to apply before you can fully render your expression.