如何在Proxy上进行模式匹配?

时间:2019-02-25 07:52:45

标签: haskell

我需要一种方法来根据类的特定实例有条件地应用函数。

我尝试使用Proxy对其输入类型进行注释:

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

但是在第5行上出现“非法类型签名:'Proxy Int'错误”。

我是否应该使用其他一些机制,例如标记的,可键入的...?

2 个答案:

答案 0 :(得分:3)

  

代理是一种不保存数据的类型,但具有任意类型(甚至种类)的幻像参数。

即在运行时,您无法确定自己是否有Proxy Int,因此无法对此进行模式匹配。而不是Proxy,您需要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

TypeApplications需要@Int)。您也可以使用typeOf (0 :: Int)

编辑:这会做什么?参见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

答案 1 :(得分:3)

你的意思是这样吗?

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"

在这里,我们使用Typeable在运行时检查ab是否是同一类型,这可以让我们应用f还是不应用。