【问题标题】:How to pattern match on Proxy?如何在代理上进行模式匹配?
【发布时间】:2019-02-25 18:08:13
【问题描述】:

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

我尝试使用 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 行收到“非法类型签名:“代理 Int”错误。

我是否应该使用其他一些机制,例如 Tagged、Typeable、...?

【问题讨论】:

  • 要放置该类型签名,您需要ScopedTypeVariables。实例 decl 中的 Proxy Int 比实例头部的预期更具体。试试applyIf (p :: proxy Int) f b = ...
  • 您是否尝试对类型进行模式匹配?从这段代码片段中很难看出你的最终目标是什么。
  • applyIf (Proxy:: Proxy Int) (*2) "ok" 不应该进行类型检查,因为 bString 并且 (*2) 不会返回 String
  • 我需要在类定义中将 (a -> b) 替换为 (a ->a)。

标签: haskell


【解决方案1】:

你的意思是这样的吗?

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

【讨论】:

    【解决方案2】:

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

    即在运行时,您无法判断您是否有Proxy Int,因此无法对此进行模式匹配。你需要TypeRep,而不是Proxy

    {-# 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
    

    (对于@Int,您需要TypeApplications)。你也可以使用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
    

    【讨论】:

    • 就像在 OP 中一样,otherwise 案例是无法访问的,这就是为什么我想知道真正的意图是什么。
    • 是的,一开始我没有注意到。我的猜测是instance ApplyIf a,但这不会让最后一行有意义。
    • 我现在将 (a->b) 替换为 (a->a)。尽管如此,您的代码仍无法编译,因为 TypeRep 有种类 '*',而不是 '* -> *'。
    • @user474491 你的 GHC 版本是多少? (并且f b 现在尝试将a -> a 应用于b)。
    • 啊,我需要导入Type.Reflection,而不是Data.Typeable。
    猜你喜欢
    • 1970-01-01
    • 2021-04-08
    • 1970-01-01
    • 2015-04-02
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多