【问题标题】:Pattern matching where the pattern is based on a (function) parameter模式匹配,其中模式基于(函数)参数
【发布时间】:2012-11-29 14:37:01
【问题描述】:

我想写一个同时接受两者的函数

  • 某个代数数据类型的值构造函数,以及
  • 同一类型的实际值,

并确定给定值是否“由”给定构造函数“制成”。模式匹配似乎很适合这种情况,但要匹配的模式必须是函数参数,而不是硬编码的构造函数名称。

下面的代码是我尝试过的,但是 GHC 在指示的行上报告了一个解析错误。

有没有办法做到这一点?

data FooBar = Foo Int | Bar String

-- Imagine that these are useful functions.
processInt :: Int -> String
processInt = show
processString :: String -> String
processString = id

-- This should take one of the above functions and adapt it to operate on
-- FooBar values of compatible "type".  Values that match the given FooBar
-- constructor should be "unwrapped" and passed to the given function.
typeCheck :: (a -> FooBar) -> (a -> String) -> (FooBar -> Maybe String)
typeCheck constructor func fooBar = case fooBar of
  (constructor x) -> Just (func x)  -- GHC says "Parse error in pattern: constructor"
  _ -> Nothing

-- Define processing functions that operate on FooBars.
processFoo :: FooBar -> Maybe String
processFoo = typeCheck Foo processInt
processBar :: FooBar -> Maybe String
processBar = typeCheck Bar processString

【问题讨论】:

    标签: generics haskell pattern-matching


    【解决方案1】:

    有趣的想法。我想知道您要做什么,因为这是一个非常不寻常的模式匹配问题。

    如果可以的话,当然可以:

    • 枚举类型的构造函数
    • 类型的元素相等

    像这样(我打破了f部分的应用,因为那是正交的):

    wasBuilt :: Eq t => (t -> Either t t)   -- ^ the constructor
                     -> Either t t          -- ^ a value
                     -> Maybe t             -- ^ the transformed result
    
    wasBuilt k v = case v of
        Left  x | v == k x    -> Just x
        Right x | v == k x    -> Just x
        _                     -> Nothing
    

    但是有很多样板。这个问题让我感到“泛型”。尝试一种不同的方法,并将构造函数反映到数据,然后可能在该数据上进行一般匹配。这将允许您将构造函数视为值,而不是函数。


    这大致是我的想法,但请注意,这是一种高级技术。常规 AST 上的显式模式匹配更加惯用:

    import Generics.SYB
    
    -- only works for unary constructors
    sameConstructor :: (Data a, Data b) => (a -> b) -> b -> Bool
    sameConstructor k v = toConstr v == toConstr (k undefined)
    
    > sameConstructor (Left :: Char -> Either Char Char) (Right 'x')
    False
    
    > sameConstructor (Left :: Char -> Either Char Char) (Left 'x')
    True
    
    > sameConstructor (:[]) "haskell"
    True
    

    【讨论】:

    • 这是一个 RPN 计算器(练习程序),可以在堆栈上混合不同类型(例如数字和字符串)。大多数在堆栈上操作的函数都会从弹出一些值开始,检查它们是否属于操作的正确类型,如果不正确则报告错误。我正在尝试排除类型检查以避免代码重复。
    • 啊!如果您将类型标签编码为简单值(例如Int 值)而不是构造函数,则会容易得多。这就是让您的示例如此棘手的原因:函数的模式匹配非常困难。
    • 谢谢,我会考虑改用数字类型标签。由于我的类型集相对有限,我也可以创建一系列函数,如 typeCheckInttypeCheckString 等,并在每个函数中硬编码适当的构造函数。由于这是一个练习项目,我想我会尝试一些更具挑战性的事情,但听起来我可能咬得比我能咀嚼的多。我熟悉 C++ 和 Java 上下文中的泛型,但不熟悉 Haskell 上下文。
    • 不,编写 RPN 计算器很容易。通过尝试对函数进行模式匹配,您刚刚进入了一个有趣的设计角落。在运行时对类型标签使用简单类型。如果是good enough for Simon Peyton Jones,你可能也可以 :-) Peyton Jones [1987, Chapter 10]
    • 我所说的“比我能咀嚼的多”我只是指这个模式匹配问题,而不是整个计算器——我是在 HP48 的 RPL 环境之后对其进行建模,我认为这具有相当的挑战性,但仍然可以实现. :-) 不过,我不确定如何避免对值使用代数类型;我使用List 作为堆栈,所以它不能是异构的。或者您是否建议在代数类型的addition 中使用数字标签?
    【解决方案2】:

    您可能想看看Type-safe pattern combinators 功能性珍珠。虽然它不能很好地与 Haskell 的模式匹配语法混合,但它确实允许您拥有可组合的一流模式的模块化,如果这是您所需要的(即,如果增加的可组合性超过了语法上的不便)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-02
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多