packages feed

AutoForms-0.4.0: src/Graphics/UI/AF/General/InstanceCreator.hs

{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses
  , OverlappingInstances, UndecidableInstances #-}

{-# OPTIONS -fglasgow-exts #-}

module Graphics.UI.AF.General.InstanceCreator
    ( createInstance, createInstance'
    , gInstanceCreatorCtx, GInstanceCreator, gGenUpTo
    )

where

import Graphics.UI.AF.General.MySYB
-- import Graphics.UI.AF.General.CustomTypes

-- The function below is heavily inspired of "Test-data generator"
-- example from SYB2 paper.
--
-- |Creates an instance of a Haskell type. For this to work the compiler
--  must be able to deduce the type from the callee's context.
createInstance :: GInstanceCreator a => Maybe a
createInstance = helper 1  -- Zero newer returns anything
    where
      --helper :: Int -> a
      helper 8   = Nothing   -- Make sure we do not to loop eternally or for a very, very long time.
      helper x   = if (length generate) == 0
                     then helper (x+1)
                     else Just $ head generate
          where
            generate = (gGenUpToD dict) x

-- |Like 'createInstance' excepts it uses a phantom type to elicit the
--  correct type to return.
createInstance' :: GInstanceCreator a
                => a          -- ^Not evaluated. Only used to force the right type.
                -> Maybe a
createInstance' _ = createInstance


-- |The dictionary type for the GInstanceCreator class
data GInstanceCreatorD a = GInstanceCreatorD { gGenUpToD  :: Int -> [a] }

-- |Instantiation of the Sat class
instance GInstanceCreator a => Sat (GInstanceCreatorD a)
  where  dict = GInstanceCreatorD { gGenUpToD  = gGenUpTo }

-- |The context for generic autoform
gInstanceCreatorCtx :: Proxy GInstanceCreatorD
gInstanceCreatorCtx = error "gInstanceCreatorCtx"

-- |Used to creates instances of data types
class (Data GInstanceCreatorD a) => GInstanceCreator a
    where
    -- This code is heavily inspired of the "Test-data generator" example in the SYB2 paper.

    -- |Generates all possible instances of a, while using no more
    -- than n levels of recursion. Each subtype requires another level
    -- of recursion. For example:
    --
    -- Branch (Branch Leaf 17) (Leaf 3)
    --
    -- would require 4 levels of recursion. One for the first branch,
    -- one for second branch, one for the left Leaf, and one for the
    -- Int (the seventeen). The right part of the first branch (Left
    -- 3) would be done in two recursions.
    gGenUpTo :: Int   -- ^ Max number of recursions
             -> [a]
    gGenUpTo 0 = []
    gGenUpTo d = result
        where
          -- Recurse per possible constructor
          result = concat (map recurse cons)
          -- Retrieve constructors of the requested type
          -- cons :: (Data ctx a) => ctx() -> [Constr]
          cons = case dataTypeRep ty of
                   AlgRep cs   -> cs
                   IntRep      -> [mkIntConstr ty 0]
                   FloatRep    -> [mkFloatConstr ty 0]
                   StringRep   -> [mkStringConstr ty "f"] -- Also used for char, so we changed foo to f
                                                          -- Or Maybe SYB3/Instances.hs should be changed
                   NoRep       -> [] -- error "InstanceCreator: NoRep"
              where
                ty = dataTypeOf gInstanceCreatorCtx (head result)
          -- Find all terms headed by a specific Constr
          recurse :: Constr -> [a]
          recurse = fromConstrM gInstanceCreatorCtx ((gGenUpToD dict) (d-1))

{-
instance GInstanceCreator Bool
instance GInstanceCreator Int
instance GInstanceCreator Char
instance GInstanceCreator Float
instance GInstanceCreator Double
instance (GInstanceCreator a) => GInstanceCreator (Maybe a)
instance (GInstanceCreator a) => GInstanceCreator [a]
instance GInstanceCreator AFFilePath
instance GInstanceCreator AFDirectoryPath
-}

instance (Data GInstanceCreatorD a) => GInstanceCreator a