Tuesday, May 31, 2022

Generate web forms from pure functions

Generate web forms from pure functions

This is an announcement post for my “Grace browser” project, which you can find here:

This project is a web page which can dynamically convert a wide variety of functional programming expressions to their equivalent HTML. This conversion can even auto-generate interactive web forms from functions, which means that people without web programming knowledge can use the Grace browser to create and share simple and interactive web pages.

Demo

You can quickly get the gist of this project by visiting the following page:

The Grace browser begins with a code editor where you can input a purely functional expression and the above link uses the following example code:

\input ->
    { "x or y" : input.x || input.y
    , "x and y": input.x && input.y
    }

The interpreter then infers the type of the input expression, which for the above example is:

{ "x": Bool, "y": Bool } -> { "x or y": Bool, "x and y": Bool }

… and you can read that type as saying:

This is a function whose input is a record and whose output is also a record. The input record has two boolean-valued fields named “x” and “y” and the output record has two boolean-valued fields named “x or y” and “x and y”.

Here is the novel bit: the interpreter then generates a web interface appropriate for that type. In this case, the equivalent web interface is a form with two inputs:

  • a checkbox labeled “x”
  • a checkbox labeled “y”

… and the form also produces two outputs:

  • a read-only checkbox labeled “x or y”
  • a read-only checkbox labeled “x and y”

The result looks like this:

Screen Shot 2022-05-28 at 11 12 23 AM

Moreover, the generated form is reactive: as you check or uncheck the two input checkboxes the two output checkboxes immediately update in response. In particular:

  • the “x or y” box will be checked whenever either input box is checked
  • the “x and y” box will be checked whenever both input boxes are checked

Screen Shot 2022-05-28 at 11 19 15 AM

This also all runs entirely client side, meaning that all the computation happens in your browser. Specifically:

  • compiling the functional code to an interactive form is done client-side
  • updating form outputs in response to inputs is also done client-side

Intelligent forms

Here’s another example that might further pique your interest:

The above example is a pure function to increment each element of a list:

List/map (\x -> x + 1)

The List/map function is unsaturated, meaning that we’re missing the final argument: the actual list to transform. So the interpreter infers the following type for the function:

List Natural -> List Natural

… and you can read that type as saying:

This is a function whose input is a list of natural numbers and whose output is also a list of natural numbers.

So what kind of interactive form will that generate?

The generated input to the form begins with a blue “+” button that you can click to add elements to the input list. Each time you click the button the form creates a numeric input for that list element alongside a red “-” button that you can click to delete the corresponding list element. As you add or remove elements from the input list the reactive form update will also add or remove elements from the output list, too.

Screen Shot 2022-05-28 at 11 14 45 AM

Moreover, each element in the input list will be a numeric input. As you adjust each input element the matching output element will automatically be set to a number that is one greater than the input number.

The interpreter also sets the permitted range of the numeric inputs based on the inferred type. Since the default numeric type is Natural (i.e. non-negative) numbers, the numeric inputs will forbid negative inputs. However, if you were to add a type annotation to specify an Integer element type:

List/map (\x -> x + 1 : Integer)

… then the generated form will change to permit negative inputs and outputs because then the inferred type would be:

List Integer -> List Integer

Shareable forms

The “Grace browser” is based on the Fall-from-Grace functional programming language (or “Grace” for short), which I previously announced here:

… and one of the features of this language is the ability to import arbitrary expressions by URL, including functions!

That means that if you were to create a useful pure function for others to use you could host your code anywhere that you can serve plain text (such as a GitHub repository or gist) and anybody could turn that function into the corresponding form.

For example, the following gist contains a pure Grace function to compute US federal income taxes for 2022:

… so if you paste the URL for the raw text of the gist into the Grace browser you’ll get a shareable form for computing your taxes:

Screen Shot 2022-05-28 at 11 15 45 AM

This provides a lightweight way to publish, share, and consume utility code.

Plain JSON data

The Grace browser also works for plain data, too. In fact, Grace is a superset of JSON so the Grace browser is also a JSON browser.

For example, I can render the JSON served at https://api.github.com as HTML by pasting that URL into the Grace browser, which produces this result:

Screen Shot 2022-05-28 at 11 16 39 AM

If I’m only interested in one field of the JSON output, I can project out the field of interest like this:

( https://api.github.com/ ).starred_gists_url

Screen Shot 2022-05-28 at 11 17 08 AM

In other words, you can use Grace browser sort of like a “jq but with HTML output”.

Teaching tool

You can also use the Grace browser to teach functional programming concepts, too. For example, you can illustrate the difference between Haskell/Rust-style error-handling and Go-style error-handling by entering this input into the code editor:

{ "Error handling in Haskell and Rust":
    \input -> input : < Failure : Text | Success : Natural >
, "Error handling in Go":
    \input -> input : { "Failure" : Optional Text, "Success": Optional Natural }
}

… and the form will illustrate how:

  • In Haskell and Rust, the failure data and success data are mutually exclusive

    The generated form illustrates this by using mutually exclusive radio buttons for the input.

  • In Go, failure data and success data are not mutually exclusive

    The generated form illustrates this by using checkboxes to independently enable or disable the failure and success data.

Screen Shot 2022-05-28 at 11 18 06 AM

Semantic web

Grace is currently very limited in its current incarnation, meaning that the language only provides a small set of built-in functions and operators. The reason why is because I originally created Grace to serve as a simple reference implementation of how to create a functional programming language and I intended people to fork the language to add any additional language features they needed.

However, if Grace were more featureful then you could imagine creating a “semantic web” of purely functional expressions that could reference each other by URL and be visualized using an intelligent client like the Grace browser. For example, you could have some pure data hosted at https://example.com/students.ffg:

[ { "Name": "John Doe"       , "Grade": 95 }
, { "Name": "Mary Jane"      , "Grade": 98 }
, { "Name": "Alice Templeton", "Grade": 90 }
]

… and then you could create a “view” into that data that adds up all the grades by hosting another expression at https://example.com/total.ffg which could contain:

let sum = https://raw.githubusercontent.com/Gabriella439/grace/main/prelude/natural/sum.ffg

in  sum (List/map (\student -> student."Grade") ./students.ffg)

… and whenever you would update the data hosted at students.ffg the view at total.ffg would automatically update, too. You could then generate a web page for either view of the data using something like the Grace browser.

Conclusion

If this interests you, the website contains a tutorial that you can try out that partially overlaps with the examples from this post:

Just click the “Try the tutorial” button to give it a whirl.

If you want a deeper dive into what the Grace language can do, then I recommend reading the original announcement post:

… or reading the README from the corresponding GitHub project:

Also, I set things up so that if you do fork the language you can easily generate your own “Grace browser” for your fork of the language that’s just a bunch of static assets you can host anywhere. No server-side computation necessary!

No comments:

Post a Comment