Skip to content

Task Structure

patritzenfeld edited this page Oct 6, 2025 · 7 revisions

A Flex-Tasks configuration consists of multiple distinct Haskell modules. Each module needs to define a specific required function or value that will be executed at runtime to obtain the necessary components for Autotool’s task interface. Aside from these mandatory functions and values, you're free to include arbitrary code in any of the modules.

Modules

A configuration must always contain five fixed modules that are expected to contain a specific interface. It can optionally contain any number of additional modules afterwards. The table below lists each required module, the mandatory function or value it must contain, and its role in the configuration:

Required Module Interface Use
Global - Define shared types and functionality to use in other modules.
TaskSettings validateSettings Define configuration constants; Validate selected constants.
TaskData getTask Generate distinct task values and matching input forms for each student; Partially precompute Check.
Check checkSyntax, checkSemantics (names subject to change) Display student-specific feedback given a submission.
Description description Display student-specific task descriptions.
Parse parseSubmission Read student submissions.

Task Identifier

Each task can be given a name in a separate, optional section of the configuration. If provided, this identifier is used as a file path for Autotool's internal caching, decreasing the risk of overlap between two independent tasks. The task will use a shared global cache if the section is omitted.

Configuration Structure and Syntax

A configuration is split into modules by lines of three or more consecutive equals signs (e.g., ===). The modules themselves are written in standard Haskell syntax.

The following diagram displays the mandatory fragments of a configuration in a more detailed manner than the previous table. It shows simplified type signatures for all interfaces and illustrates how the modules relate structurally:

Task structure diagram

Quasi-Quoting

In the diagram, you can see that the entire Check module is actually a simple String contained inside TaskData. This allows for precomputing values ahead of time and embedding them into the code before the Check module gets interpreted. Flex‑Tasks uses Haskell’s QuasiQuotes extension to embed multi‑line strings directly into modules. This simplifies composing long Strings with indents and multiple variables. Quasi-quoted Strings are written using the [i| ... |] syntax.

For example, this:

{-# language QuasiQuotes #-}

[i|
This is interpreted as a verbatim String.
  Line breaks and spaces are taken into account.
We can also directly insert showable values, like #{84*37}!
String symbols like "" work without escaping,
but some symbols like \# or \\ have special meanings in a quasi-quoter.
|]

is evaluated to the following String:

 "This is interpreted as a verbatim String.\n" ++
 "  Line breaks and spaces are taken into account.\n" ++
 "We can also directly insert showable values, like 3108!\n" ++
 "String symbols like \"\" work without escaping,\n" ++
 "but some symbols like # or \\ have special meanings in a QuasiQuoter."

← Previous: Overview | Next: Getting Started →

Clone this wiki locally