Reputation: 341
I want to idenfity what function was passed as parameter to a high-order function. How can i do that? Using pattern matching? I want to do something like the following code:
add x y = x+y
sub x y = x-y
myFunc :: (a->a->a) -> a -> a -> IO a
myFunc add x y = do print "add was performed"
add x y
myFunc sub x y = do print "sub was performed"
sum x y
myFunc f x y = do print "another function was performed"
f x y
If this is not possible, does anyone has other idea to do that?
Upvotes: 3
Views: 171
Reputation: 48611
It's not possible to do exactly what you requested. I would recommend that you instead make an embedded domain-specific language (EDSL) and write one or more interpreters for it. The most common approach is to represent the EDSL using an algebraic datatype or (in more complicated situations) a generalized algebraic datatype. Here you might have something like
data Expr a = Lit a
| BinOp (Op a) (Expr a) (Expr a)
deriving (Show)
data Op a = Add
| Sub
| Other (a -> a -> a)
instance Show (Op a) where
show Add = "Add"
show Sub = "Sub"
show Other{} = "Other"
Now you can write an evaluator that takes an Expr a
and performs the requested operations:
evalExpr :: Num a => Expr a -> a
evalExpr (Lit x) = x
evalExpr (BinOp op e1 e2) = runOp op (evalExpr e1) (evalExpr e2)
runOp :: Num a => Op a -> a -> a -> a
runOp Add a b = a + b
runOp Sub a b = a - b
runOp (Other f) a b = f a b
You can add tracing too:
evalExpr' :: (Num a, MonadWriter [(Expr a, a)] m) => Expr a -> m a
evalExpr' e = do
result <- case e of
Lit a -> return a
BinOp op e1 e2 -> runOp op <$> evalExpr' e1 <*> evalExpr' e2
tell [(e, result)]
return result
Sample use:
*Write> runWriter $ evalExpr' (BinOp Add (Lit 3) (BinOp Sub (Lit 4) (Lit 5)))
(2,[(Lit 3,3),(Lit 4,4),(Lit 5,5),(BinOp Sub (Lit 4) (Lit 5),-1),(BinOp Add (Lit 3) (BinOp Sub (Lit 4) (Lit 5)),2)])
For convenience, you can write
instance Num a => Num (Expr a) where
fromInteger = Lit . fromInteger
(+) = BinOp Add
(-) = BinOp Sub
Then the above can be abbreviated
*Write Control.Monad.Writer> runWriter $ evalExpr' (3 + (4-5))
(2,[(Lit 3,3),(Lit 4,4),(Lit 5,5),(BinOp Sub (Lit 4) (Lit 5),-1),(BinOp Add (Lit 3) (BinOp Sub (Lit 4) (Lit 5)),2)])
Upvotes: 3
Reputation: 463
Maybe to simplify and not to change a lot the overall look of your code, if it's already a long project and that's a concern, you could do something like:
add x y = x+y
sub x y = x-y
myFunc :: (Eq a, Num a) => (a->a->a) -> a -> a -> IO a
myFunc f x y = if (add x y) == (f x y) then
do print "add was performed"
return (add x y)
else if (sub x y) == (f x y) then
do print "sub was performed"
return (sub x y)
else
do print "another function was performed"
return (f x y)
It works, the only problem is that you wont be able to diferentiate for example an add 2 1 from a multiplication 2 1, so if thats a possibility you can throw new cases in there to cover all important grounds, like instead of only comparing add x y = f x y, also compare add y x with f y x. With some thought it will work 100% of the time.
Upvotes: 1
Reputation: 94429
No, this is not possible.
You could achieve something to that effect by having a data type which represents the operation, maybe
data Operation
= Add (a -> a -> a)
| Sub (a -> a -> a)
| Other (a -> a -> a)
myFunc :: Operation -> a -> a -> IO a
myFunc (Add f) x y = do print "add was performed"
return (f x y)
myFunc (Sub f) x y = do print "sub was performed"
return (f x y)
myFunc (Other f) x y = do print "another function was performed"
return (f x y)
Upvotes: 10