module Calculator.Prim.Function ( Function (..) , mkFunc , mkFuncEither , oneArg , partial , testFunc ) where -------------------------------------------------------------------------------- -- | Represents a function, where rank == No. of arguments data Function = Function { arity :: Int , apply :: [Double] -> Either [String] Double } -------------------------------------------------------------------------------- -- | Make a @Function@ out of an arity and another function mkFunc :: Int -> ([Double] -> Double) -> Function mkFunc a f = mkFuncEither a (Right . f) -------------------------------------------------------------------------------- -- | Make a @Function@ with an arity using an error giving function mkFuncEither :: Int -> ([Double] -> Either [String] Double) -> Function mkFuncEither a f = Function { arity = a , apply = g } where g xs = if length xs == a then f xs else Left [ "Invalid no. of arguments" , "Required: " ++ show a , "Provided: " ++ show (length xs) ] -------------------------------------------------------------------------------- -- | Make a @Function@ out of a single argument function oneArg :: (Double -> Double) -> Function oneArg f = mkFunc 1 (\ [x] -> f x) -------------------------------------------------------------------------------- -- | Partially apply a function, thus creating a one-argument function. -- To be used for parametrized functions, e.g getting g(x) = f(x,1,2) -- this retains the free-variable 'x' partial :: Function -> [Double] -> Double -> Maybe Double partial f xs x = case apply f (x:xs) of Left _ -> Nothing Right v -> Just v -------------------------------------------------------------------------------- -- | Test a @Function@ with some value t, useful for sanity checking -- while adding a @Function@ to a @Bindings@ testFunc :: Function -> Double -> Maybe [String] testFunc f t = case apply f (replicate (arity f) t) of Left ms -> Just ms Right _ -> Nothing --------------------------------------------------------------------------------