【问题标题】:Omitting constructor arguments in Haskell case statements在 Haskell 案例语句中省略构造函数参数
【发布时间】:2014-03-21 05:50:17
【问题描述】:

省略函数参数是简洁 Haskell 代码的好工具。

h :: String -> Int
h = (4 +) . length

如果在 case 语句中省略数据构造函数参数会怎样。下面的代码可能会被认为有点邋遢,其中siAB 中的最终参数,但在每个案例匹配的正文中作为最终参数重复。

f :: Foo -> Int
f = \case
     A s -> 4 + length s
     B i -> 2 + id i

有没有办法在案例模式匹配中省略这些参数?对于具有大量参数的构造函数,这将从根本上缩短代码宽度。例如。以下伪代码。

g :: Foo -> Int
g = \case
     {- match `A` constructor -> function application to A's arguments -}
     A -> (4 +) . length
     {- match `B` constructor -> function application to B's arguments -}
     B -> (2 +) . id

【问题讨论】:

    标签: haskell


    【解决方案1】:

    GHC 扩展 RecordWildCards 让您可以简洁地将构造函数的所有字段纳入范围(当然,这需要您为这些字段命名)。

    {-# LANGUAGE LambdaCase, RecordWildCards #-}
    
    data Foo = Foo {field1, field2 :: Int} | Bar {field1 :: Int}
    
    baz = \case
        Foo{..} -> 4 + field2
        Bar{..} -> 2 + field1
    
    -- plus it also "sucks in" fields from a scope
    mkBar400 = let field1 = 400 in Bar{..}
    

    `

    【讨论】:

    • 你也可以只写Foo {}Bar {}来匹配构造函数,不需要任何扩展。请注意,即使您不使用数据类型的记录语法,这也有效。
    • @kosmikus 如果不使用记录语法,我如何将构造函数参数放在箭头右侧的正确位置?
    • 我从未见过它可以从当前范围内“吸入”字段,这可能真的很有用!
    • @RobStewart 你不会的。
    • @RobStewart 如果您使用{},则无法引用参数。有时,您只想在构造函数上进行区分,写Foo {} 比写Foo _ _ _ _ 更容易。但这不是您问题的一般解决方案,这就是为什么我只是将其添加为对此答案的评论。
    【解决方案2】:

    您始终可以将构造函数的 case 语句重构为单个函数,以便从那时起您只需将简洁的函数定义作为参数传递给这些特定函数。请允许我举例说明。

    考虑Maybe a 数据类型:

     data Maybe a = Nothing | Just a
    

    你现在是否需要定义一个函数f :: Maybe a -> b(对于一些固定的b,也许还有a),而不是像这样写

     f Nothing = this
     f (Just x) = that x
    

    你可以先定义一个函数

     maybe f _ Nothing = f
     maybe _ g (Just x) = g x
    

    然后f 可以定义为maybe this that。几乎与所有熟悉的递归模式发生的情况一样。

    通过这种方式,您可以有效地重构 case 语句。可以说代码变得更简洁,并且不需要语言扩展。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-08
      • 1970-01-01
      • 1970-01-01
      • 2016-08-19
      • 1970-01-01
      • 2018-08-06
      • 2023-03-03
      • 1970-01-01
      相关资源
      最近更新 更多