packages feed

multivariant-0.1.0.1: app/Task.hs

{-|
Module      : Task
Description : Example usage of library
Copyright   : (c) Anton Marchenko, Mansur Ziatdinov, 2016-2017
License     : BSD-3
Maintainer  : gltronred@gmail.com
Stability   : experimental
Portability : POSIX

This module provides example of 'task'.

This task is the following one.

<<doc/diagram.png Task example>>

Part 'alpha' adds 5 to each list element.

Part 'beta' has two variants: it either sums all list elements or computes product.

Part 'gamma' takes a list and a number and multiplies every list element to this number.

Part 'delta' is either sum or product of given list.
-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}

module Task
  ( task
  , Input
  , Output
  , inputVariants
  , printTask
  ) where

import           Task.Pretty
import           Task.Types

import           Data.Invertible.Bijection
import           Prelude (Integer, (+), (-), (*), ($))
import qualified Prelude as P
import           Data.Invertible.List

---------------------------------------------------------------------
-- EXAMPLE OF TAGLESS FINAL APPROACH
---------------------------------------------------------------------

import           Test.Multivariant.Classes

type P prog a b = (WithDescription prog, WithCornerCases prog) => prog a b

-- TASK DESCRIPTION BEGINS HERE

-- | Part alpha. Adds 5 to each list element
--
-- >  step (Inv.map $ (\x -> x+5) :<->: (\x -> x-5))
--
-- We use 'Data.Invertible.List.map' and 'Data.Invertible.Bijection.(:<->:)'
alpha :: P prog [Integer] [Integer]
alpha = step (map $ (\x -> x+5) :<->: (\x -> x-5))
        `withCornerCases` ([[],[-1,5],[5,4]],
                           [])
        `withDescription` "Add 5 to each element of the list"

-- | Part beta. Either sum or product of given list
--
-- >  step (sum :<->: (\x -> [x,0]))
--
-- 'Prelude.sum' is not invertible, so we use a (right) inverse.
beta :: P prog [Integer] Integer
beta = oneof [beta1, beta2]
  where beta1 = step (P.sum :<->: (\x -> [x,0]))
                `withCornerCases` ([[],[3,2]],
                                   [0])
                `withDescription` "Compute sum of elements of list"
        beta2 = step (P.product :<->: (\p -> [p,1]))
                `withDescription` "Compute product of elements of list"
                `withCornerCases` ([[],[0]],
                                   [])

-- | Part gamma.
--
-- >  step ((\(xs,y) -> map (*y) xs) :<->: (\ys -> (ys,1)))
--
-- We use a (right) inverse @(\ys -> (ys,1))@.
gamma :: P prog ([Integer],Integer) [Integer]
gamma = step ((\(xs,y) -> P.map (*y) xs) :<->: (\ys -> (ys,1)))
        `withCornerCases` ([ ([],1), ([1,2],0), ([],0), ([1,2],2)],
                           [ ])
        `withDescription` "Multiply each element of result of first operation to result of second operation"

-- | Part delta.
--
-- Either sum or product of list
delta :: P prog [Integer] Integer
delta = delta1 <+++> delta2
  where delta1 = step (P.product :<->: (\p -> [p,1]))
                 `withDescription` "Compute product of elements of list"
                 `withCornerCases` ([[],[0]],
                                    [])
        delta2 = step (P.sum :<->: (\s -> [s,0]))
                 `withDescription` "Compute sum of elements of list"
                 `withCornerCases` ([[]],
                                    [0])

-- | Combined task
task :: P prog Input Output
task = (alpha <***> beta) ~> gamma ~> delta

-- TASK DESCRIPTION ENDS HERE

-- | Inputs to be fed to example solution (see 'Task.Pretty.printTask')
inputVariants :: [Input]
inputVariants = 
  [ ([1,2,3], [1,2])
  , ([1,-1], [1,2])
  , ([], [1,2,1,4])
  , ([0,1,2], [1,-1])
  ]