Reputation: 2625
I need a method to conditionally apply a function depending on the specific instance of a class.
I tried using Proxy to annotate the function with the type of its input:
class ApplyIf b where
applyIf :: Show b => proxy a -> (a -> a) -> b -> String
instance ApplyIf Int where
applyIf (p :: Proxy Int) f b = show (f b)
applyIf _ _ b = show b
instance ApplyIf String where
applyIf _ _ b = show b
main = do
putStrLn $ applyIf (Proxy:: Proxy Int) (*2) 1 -- 2
putStrLn $ applyIf (Proxy:: Proxy Int) (*2) "ok" -- ok
But I get an 'Illegal type signature: 'Proxy Int' error on line 5.
Should I use some other mechanisms, like Tagged, Typeable, ... ?
Upvotes: 2
Views: 421
Reputation: 170839
Proxy is a type that holds no data, but has a phantom parameter of arbitrary type (or even kind).
I.e. at runtime you can't tell if you have a Proxy Int
and so can't pattern match on this. Instead of Proxy
you need TypeRep
:
{-# LANGUAGE TypeApplications #-}
import Type.Reflection
class ApplyIf a where
applyIf :: Show b => TypeRep a -> (a -> a) -> b -> String
instance ApplyIf Int where
applyIf tr f b | tr == typeRep @Int = show (f b)
| otherwise = show b
(you need TypeApplications
for @Int
). You could use typeOf (0 :: Int)
as well.
EDIT: would this do what you want? See Data.Type.Equality
.
{-# LANGUAGE TypeApplications #-}
import Type.Reflection
import Data.Type.Equality
-- could have Typeable a constraint instead of a TypeRep a param
applyIf :: (Show b, Show c, Typeable b) => TypeRep a -> (a -> c) -> b -> String
applyIf tr f b =
case testEquality tr (typeOf b) of
Just Refl -> show (f b)
Nothing -> show b
main = do
putStrLn $ applyIf (typeRep @Int) (*2) 1 -- 2
putStrLn $ applyIf (typeRep @Int) (*2) "ok" -- ok
Upvotes: 3
Reputation: 85877
Do you mean something like this?
import Data.Typeable
applyIf :: (Show a, Typeable a, Show b, Typeable b) => (b -> b) -> a -> String
applyIf f x = case cast x of
Nothing -> show x
Just y -> show (f y)
main = do
putStrLn $ applyIf ((*2) :: Int -> Int) (1 :: Int)
putStrLn $ applyIf ((*2) :: Int -> Int) "ok"
Here we use Typeable
to check at runtime whether a
and b
are the same type, which either lets us apply f
or not.
Upvotes: 3