Skip to content

Getting Started

patritzenfeld edited this page Oct 6, 2025 · 11 revisions

Writing a Flex-Tasks configuration from scratch can feel daunting. It’s usually easier to start by editing an existing file: either the template shown on this page or a working configuration already used in Autotool.

Task Template

The following task template is a minimal functioning configuration you can directly paste into Autotool. The task itself is intentionally simple and a bit nonsensical, but it serves as a basic example of how to write a configuration. The full version of this template includes inline commentary, which has been removed here for brevity. You can have a look at the full version (also the default in Autotool), if you're interested.

Toggle task template view
taskName: DefaultConfig
=============================================

module Global where


type Submission = (Int,Int)
type DescData = (Int,Int,Int)
type TaskData = (DescData,Submission)

=============================================

module TaskSettings where


import Control.OutputCapable.Blocks (LangM, OutputCapable)


validateSettings :: OutputCapable m => LangM m
validateSettings = pure ()

=============================================

{-# language OverloadedStrings #-}
{-# language QuasiQuotes #-}

module TaskData (getTask) where


import FlexTask.GenUtil        (fromGen)
import FlexTask.Generic.Form
import FlexTask.YesodConfig    (Rendered, Widget)
import Control.Monad.Random    (MonadRandom)
import Data.String.Interpolate (i)
import Test.QuickCheck.Gen
import Yesod                   (RenderMessage(..), fieldSettingsLabel)

import Global



data Label = Product | Sum


instance RenderMessage a Label where
  renderMessage _ ("de":_) Product = "Produkt"
  renderMessage _ ("de":_) Sum     = "Summe"
  renderMessage _ _        Product = "Product"
  renderMessage _ _        Sum     = "Sum"


getTask :: MonadRandom m => m (TaskData, String, Rendered Widget)
getTask = fromGen $ do
    numbers@(n1,n2,n3) <- (,,) <$> intInRange <*> intInRange <*> intInRange
    let checkData = (product [n1,n2,n3], sum [n1,n2,n3])
    pure ((numbers, checkData), checkers, form)
  where
    intInRange = chooseInt (1,6)


fieldNames :: [[FieldInfo]]
fieldNames = [[fromLabel Product], [fromLabel Sum]]
  where
    fromLabel = single. fieldSettingsLabel


form :: Rendered Widget
form = formify (Nothing :: Maybe Submission) fieldNames


checkers :: String
checkers = [i|


{-\# language ApplicativeDo \#-}

module Check (checkSyntax, checkSemantics) where


import Control.OutputCapable.Blocks

import Global



checkSyntax :: OutputCapable m => FilePath -> TaskData -> Submission -> LangM m
checkSyntax _ _ _  = pure ()  -- nothing to check here


checkSemantics :: OutputCapable m => FilePath -> TaskData -> Submission -> Rated m
checkSemantics _ (_,sol) try
  | try == sol = pure 1.0
  | otherwise = do
      refuse $ indent $ translate $ do
        german "semantisch falsch"
        english "semantically wrong"
      pure 0.0

|]

=============================================

{-# language ApplicativeDo #-}
{-# language QuasiQuotes #-}

module Description (description) where


import Control.OutputCapable.Blocks
import Data.String.Interpolate                   (i)

import Global



description :: OutputCapable m => FilePath -> TaskData -> LangM m
description _ ((one,two,three),_) = do
  paragraph $ translate $ do
    german "Ich würfle drei Zahlen."
    english "I throw a die three times."
  paragraph $ translate $ do
    german [i|Die erste ist #{one}, die zweite ist #{two}, die letzte ist #{three}.|]
    english [i|On the first throw I roll #{one}, the second #{two} and on the third one #{three}.|]
  indent $ paragraph $ translate $ do
    german "Was sind Summe und Produkt dieser Zahlen?"
    english "What are the sum and the product of these numbers?"
  pure ()

=============================================

module Parse (parseSubmission) where


import Control.OutputCapable.Blocks (
  LangM',
  ReportT,
  OutputCapable,
  )
import FlexTask.Generic.Parse  (formParser, parseWithOrReport, reportWithFieldNumber)

import Global



parseSubmission ::
  (Monad m, OutputCapable (ReportT o m))
  => String
  -> LangM' (ReportT o m) Submission
parseSubmission = parseWithOrReport formParser reportWithFieldNumber

Ready-to-Use Configurations in Other Locations

There are a number of other Flex-Tasks configurations that are currently used for Autotool tasks. Feel free to use them as a basis for your own exercises. They can be found in the following locations:

Logic-related

Term Validation

IO Programs (private repo)

← Previous: Basic Task Structure | Next: Configuring a Task in Autotool →

Clone this wiki locally