mathlink-1.1.0.0: examples/src/Main.hs
{-# OPTIONS_GHC -O #-}
{- The -O option is necessary to get the rewrite rules which use more
efficient marshaling for certain structured types to fire.
-}
module Main where
import Foreign.MathLink
import Foreign.MathLink.Expressible
import Data.Array.Unboxed
import Data.Complex
import Control.Monad.Error (throwError)
import Control.Monad.Trans (liftIO)
import System.Timeout
-- Example 1: get a pair of integers and return their sum.
addTwo :: (Int,Int) -> IO Int
addTwo (m1,m2) = return (m1+m2)
-- Note that the desired 2-tuple of integers is represented as
-- a Mathematica list of length 2.
addTwoFunction = mkFunction -- mprep's :Pattern: directive
"AddTwo[i_Integer,j_Integer]"
-- mprep's :Arguments: directive
"{i,j}"
addTwo
-- Example 2: reverse a list of numbers (coercing them to Doubles).
reverseNumbers :: [Double] -> IO [Double]
reverseNumbers is = return $ reverse is
-- Like tuples, lists are represented on the Mathematica side as
-- lists, but, of course, of arbitrary length. Here, the Mathematica
-- pattern would match on a list of zero or more numeric values.
-- Notice that the argument pattern provides some preprocessing on the
-- arguments, taking its real part and coercing the values to
-- machine precision. This trick allows for a call pattern that
-- matches more expressions, while still ensuring that on the
-- Haskell side, the arguments have the desired, more specific,
-- form when marshaled.
reverseNumbersFunction =
mkFunction "ReverseNumbers[is___?NumericQ]"
"N[Re[{is}]]"
reverseNumbers
-- Example 3: get and put a 2D array, reversing its contents.
-- NB: When marshaling in an array, make sure that you test for the
-- array rank in callPattern to match that expected on the Haskell
-- side, as below. Otherwise, you're likely to have the underlying
-- call to fromDimensions raise an error that halts the program.
reverseArray :: UArray (Int,Int) Int -> IO (UArray (Int,Int) Int)
reverseArray arr = return $ listArray (bounds arr) $ reverse $ elems arr
reverseArrayFunction =
mkFunction "ReverseArray[a_?(ArrayQ[#,2,IntegerQ]&)]"
"a"
reverseArray
-- Example 4: gets an arbitrary Mathematica expression and returns
-- a tweaked version of it.
tweakExpression :: Expression -> IO Expression
tweakExpression expr = return $ tweak expr
where tweak ex =
case ex of
ExInt i -> ExInt (-i)
ExReal r -> ExReal (-r)
ExString s -> ExString $ reverse s
ExSymbol s -> ExSymbol $ reverse s
ExFunction hd args -> ExFunction (reverse hd) $ map tweak args
tweakExpressionFunction =
mkFunction"TweakExpression[expr_]"
"expr"
tweakExpression
-- Example 5: use your own instance of Expressible
data ExtendedComplex = Finite (Complex Double)
| Infinity
deriving (Eq,Show)
instance Expressible ExtendedComplex where
toExpression Infinity =
ExSymbol "Infinity"
toExpression (Finite (r :+ i)) =
ExFunction "Complex" [ExReal r, ExReal i]
fromExpression expr =
case expr of
ExFunction "DirectedInfinity" [_] ->
Right $ Infinity
ExFunction "Complex" [ExReal r, ExReal i] ->
Right $ Finite (r :+ i)
_ -> Left $ ExpressibleErrorMsg $
"Unexpected expression: " ++ show expr
addExtendedComplexes :: (ExtendedComplex,ExtendedComplex) -> IO ExtendedComplex
addExtendedComplexes (ec1,ec2) =
case (ec1,ec2) of
(Infinity,_) -> return Infinity
(_,Infinity) -> return Infinity
(Finite c1,Finite c2) -> return $ Finite (c1 + c2)
addExtendedComplexesFunction =
mkFunction "AddExtendedComplexes[\
\ec1:(Infinity|Complex[_,_]),\
\ec2:(Infinity|Complex[_,_])]"
"N[{ec1,ec2}]"
addExtendedComplexes
-- Example 6: check for abort
abortTest :: Int -> IO Bool
abortTest n = do
liftIO $ timeout (100000*n) (bottom)
checkAbort
where bottom = (bottom :: IO ())
abortTestFunction =
mkFunction "AbortTest[i_Integer]"
"i"
abortTest
-- run mathlink exposing the given list of actions
main = runMathLink [ addTwoFunction
, reverseNumbersFunction
, reverseArrayFunction
, tweakExpressionFunction
, addExtendedComplexesFunction
, abortTestFunction
]