mhw*_*bat 0 haskell types pattern-matching
我有这样的类型:
data MyType = I Int | C Char -- and lots of other options
我希望能够找出这种类型的值是否是特定的变体。我可以使用模式匹配来定义函数isInt、isChar等。但我宁愿只编写一个函数,如下所示:
hasBaseType :: MyType -> (a -> MyType) -> Bool
hasBaseType (f _) = True
hasBaseType _     = False
我将传递适当的构造函数(I或C)作为第二个参数。不幸的是,你不能像这样进行模式匹配。
我还想“解开”这个值。我可以再次使用模式匹配来编写函数unwrapInt、unwrapChar等。但我宁愿只编写一个函数,如下所示:
unwrap :: MyType -> (a -> MyType) -> a
unwrap (f x) = x
unwrap _     = error "wrong base type"
有没有一些奇特的魔法可以让我做到这一点?我想也许PatternSynonyms会有所帮助,但我不知道如何帮助。
我认为您会发现这些函数在实践中很笨拙,但这可以通过泛型来完成。使用Data.Data似乎最简单。您需要做的hasBaseType就是使用提供的模式构建骨架值MyType(即用作undefined字段)并比较构造函数:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data MyType = I Int | C Char deriving (Data)
hasBaseType :: MyType -> (a -> MyType) -> Bool
hasBaseType val pat = toConstr val == toConstr (pat undefined)
该unwrap函数有点棘手,但您可以查询并转换构造函数的第一个字段。fromJust这里是安全的,因为确保hasBaseType我们拥有正确的字段类型:
import Data.Maybe
unwrap :: (Typeable a) => MyType -> (a -> MyType) -> a
unwrap val pat
  | hasBaseType val pat = gmapQi 0 (fromJust . cast) val
  | otherwise = error "wrong base type"
完整代码:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
import Data.Maybe
data MyType = I Int | C Char deriving (Data)
hasBaseType :: MyType -> (a -> MyType) -> Bool
hasBaseType val pat = toConstr val == toConstr (pat undefined)
unwrap :: (Typeable a) => MyType -> (a -> MyType) -> a
unwrap val pat
  | hasBaseType val pat = gmapQi 0 (fromJust . cast) val
  | otherwise = error "wrong base type"
main = do
  print $ unwrap (C 'a') C  -- 'a'
  print $ unwrap (I 10) I   -- 10
  print $ unwrap (I 10) C   -- throws "wrong base type" error