mathlink-1.0.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. According
to the GHC documentation, it should be sufficient to set
-fenable-rewrite-rules, but this didn't work in the test cases I
examined, while -O (which implies -fenable-rewrite-rules) did.
-}
module Main where
import Foreign.MathLink
import qualified Foreign.MathLink.Expressible as Ex
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 :: IO ()
addTwo = do
(m1,m2) <- get
put ((m1 + m2) :: Int)
-- callPattern <=> mprep's :Pattern: directive
-- argumentPattern <=> mprep's :Arguments: directive
-- Note that, in argumentPattern, the desired 2-tuple of integers
-- is represented as a Mathematica list of length 2.
addTwoFunction =
Function { callPattern = "AddTwo[i_Integer,j_Integer]"
, argumentPattern = "{i,j}"
, function = addTwo
}
-- Example 2: reverse a list of numbers (coercing them to Doubles).
reverseNumbers :: IO ()
reverseNumbers = do
is <- get
put $ reverse (is :: [Double])
-- Like tuples, lists are represented on the Mathematica side as
-- lists, but of arbitrary length. Here, the Mathematica pattern
-- would accept a list of zero or more numeric values.
-- Notice that argumentPattern provides some preprocessing on the
-- arguments, taking its real part and coercing the values to
-- machine precision. This trick allows for a callPattern that
-- matches more expressions, while still ensuring that on the
-- Haskell side, the arguments have the desired, more specific,
-- form when marshaled.
reverseNumbersFunction =
Function { callPattern = "ReverseNumbers[is___?NumericQ]"
, argumentPattern = "N[Re[{is}]]"
, function = reverseNumbers
}
-- Example 3: put a string.
-- The first gives a list of single-character strings.
-- The second, an atomic string.
greetWorld :: IO ()
greetWorld = do
put ("Hello, world!", Ex.ExString "Hello, world!")
greetWorldFunction =
Function { callPattern = "GreetWorld[]"
, argumentPattern = ""
, function = greetWorld
}
-- Example 4: 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 :: IO ()
reverseArray = do
arr <- get
let xs = elems (arr :: UArray (Int,Int) Int)
put ((listArray (bounds arr) $ reverse xs) :: UArray (Int,Int) Int)
reverseArrayFunction =
Function { callPattern = "ReverseArray[a_?(ArrayQ[#,2,IntegerQ]&)]"
, argumentPattern = "a"
, function = reverseArray
}
-- Example 5: gets an arbitrary Mathematica expression and returns
-- a tweaked version of it.
tweakExpression :: IO ()
tweakExpression = do
expr <- get
put $ tweak expr
where tweak ex =
case ex of
Ex.ExInt i -> Ex.ExInt (-i)
Ex.ExReal r -> Ex.ExReal (-r)
Ex.ExString s -> Ex.ExString $ reverse s
Ex.ExSymbol s -> Ex.ExSymbol $ reverse s
Ex.ExFunction hd args -> Ex.ExFunction (reverse hd) $ map tweak args
tweakExpressionFunction =
Function { callPattern = "TweakExpression[expr_]"
, argumentPattern = "expr"
, function = tweakExpression
}
-- Example 6: use your own instance of Expressible
data ExtendedComplex = Finite (Complex Double)
| Infinity
deriving (Eq,Show)
instance Ex.Expressible ExtendedComplex where
toExpression Infinity =
Ex.ExSymbol "Infinity"
toExpression (Finite (r :+ i)) =
Ex.ExFunction "Complex" [Ex.ExReal r,Ex.ExReal i]
fromExpression expr =
case expr of
Ex.ExFunction "DirectedInfinity" [_] ->
Right $ Infinity
Ex.ExFunction "Complex" [Ex.ExReal r, Ex.ExReal i] ->
Right $ Finite (r :+ i)
_ -> Left $ Ex.ExpressibleErrorMsg $
"Unexpected expression: " ++ show expr
addExtendedComplexes :: IO ()
addExtendedComplexes = do
(ec1,ec2) <- get
case (ec1,ec2) of
(Infinity,_) -> put Infinity
(_,Infinity) -> put Infinity
(Finite c1,Finite c2) -> put $ Finite (c1 + c2)
addExtendedComplexesFunction =
Function { callPattern = "AddExtendedComplexes[\
\ec1:(Infinity|Complex[_,_]),\
\ec2:(Infinity|Complex[_,_])]"
, argumentPattern = "N[{ec1,ec2}]"
, function = addExtendedComplexes
}
-- Example 7: check for abort
abortTest :: IO ()
abortTest = do
n <- (get :: IO Int)
liftIO $ timeout (100000*n) (bottom)
checkAbort >>= put
where bottom = (bottom :: IO ())
abortTestFunction =
Function { callPattern = "AbortTest[i_Integer]"
, argumentPattern = "i"
, function = abortTest
}
-- run mathlink exposing the given list of actions
main = runMathLink [ addTwoFunction
, reverseNumbersFunction
, greetWorldFunction
, reverseArrayFunction
, tweakExpressionFunction
, addExtendedComplexesFunction
, abortTestFunction
]