λtext

General-Purpose Text-File Templating Utility

Reduce Content Duplication

λtext is a generic import tool for any text file, as an alternative to copy-paste. Why? Every time you C-c C-v, you manually duplicate the data. As soon as a change is needed, you have to edit every file you've touched. With λtext, you parameterize the files themselves, and let λtext do the pasting.

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.

Open Source

λtext is BSD Licenced - you are free to do what you want with the project. It's written in the advanced programming language Haskell, with its code available on github. Total lines of code? 972.

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. We then use those parameters by referencing their names, using the same delimiters. We are free to choose whatever syntax we want - Jade-style delimiters with {{ ... }}, Html-friendly delimiters via comments <!-- -->, etc. Here's our new and improved 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. If there's some residual text left in an inner expression, though, the result will be unprintable and an exception is thrown.

Get λtext

or

Get Stack

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 %%

asdf

%% 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 }}

asdf

Woo hoo! I'm a Text File!

{{ x }}

Debugging

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. I hope that clears up some headaches!