Haskell:有没有更好的方法用相同的RHS编写案例语句?

Haskell:有没有更好的方法用相同的RHS编写案例语句?,haskell,case,Haskell,Case,例如: data A = A B D | Aa B C | Ag B X X | Ae B R Q | Ax X getB a = case a of (A b _) -> b (Aa b _) -> b (Ag b _ _) -> b (Ae b _ _) -> b (Ax _) -> somethingElse 在Haskell中

例如:

data A =
  A B D
  | Aa B C
  | Ag B X X
  | Ae B R Q
  | Ax X

getB a = case a of 
    (A b _)         -> b
    (Aa b _)        -> b
    (Ag b _ _)      -> b
    (Ae b _ _)      -> b
    (Ax _)          -> somethingElse

在Haskell中,给定一个数据类型,其中许多构造函数具有相同的参数类型,是否有更好的方法返回此参数。或者有没有更好的方法来编写上面显示的
case
语句以减少重复?

函数级的模式匹配将有助于提高可读性,但由于这些都是不同的构造函数,因此无法一次对多个构造函数进行模式匹配(据我所知)


如果
A
具有数据实例,则可以编写

import Data.Data
mgetB :: A -> Maybe B
mgetB = gmapQi 0 cast

然后根据这个函数定义getB,一个叫做“or patterns”的特性,在ML中可用,在这方面很有帮助。五年前GHC就有了这样一个功能,但似乎没有人承担起指定细节并实际实施的任务。然而,有一种方法可以用模板Haskell来做类似的事情,正如

中所解释的那样,在某个时候,你必须说明你的
B
s是如何包含在
a
中的,所以你最好用一种通用的方法一劳永逸地完成它

bOrX a = case a of
    (A b _)         -> B' b
    (Aa b _)        -> B' b
    (Ag b _ _)      -> B' b
    (Ae b _ _)      -> B' b
    (Ax x)          -> X' x
随后,您可以用很少的代码一次匹配所有
B
s

getB a = case bOrX a of
  B' b -> b
  X' _ -> somethingElse

anotherFunctionWithBandX a = case bOrX a of
  B' b -> f b
  X' x -> g x
您可以使用和中的来简化此操作:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

-- ...

data A =
  A { _b :: B, _d :: D }
  | Aa { _b :: B, _c :: C }
  | Ag { _b :: B, _x1 :: X, _x2 :: X }
  | Ae { _b :: B, _r :: R, _q :: Q }
  | Ax { _x1 :: X }

makeLenses ''A

getB :: A -> B
getB a = case a ^? b of
  Just theB -> theB
  Nothing   -> somethingElse
对模板Haskell函数的调用
makelens
处理所有样板文件


<代码>晶状体< /代码>如果您只使用它,可能会有点依赖性,但这是值得考虑的(尤其是如果您已经使用<代码>晶状体< /代码> /考虑使用<代码>晶状体< /代码>)。

您可以使用记录语法来绕过这个:

data A =
  A {fieldB :: B, fieldC :: C} |
  Aa {fieldB :: B, fieldX1 :: X, fieldX2 :: X} |
  Ag {fieldB :: B, fieldR :: R, fieldQ :: Q} |
  Ax {fieldX :: X}

getB a = case a of
  Ax -> somethingElse
  _  -> fieldB a

关键是要给
B

类型的所有字段赋予相同的名称。这些构造器中的
b
之间没有基本的相关性;它们只是巧合地被标记为相同的东西。Related:Related:命名非记录类型字段的形式通常不好吗?有可能只做全部吗?@dfeuer我不知道你所说的“非记录类型”是什么意思,但是如果这些字段只是通过
镜头
函数访问的,那么一切都应该保持良好和全面。您可以通过不导出带下划线的名称来实现这一点。这与David Young的回答类似,只是没有添加
镜头
层。我的观点是,您不需要模板Haskell和大型复杂库。这只是记录语法。是的,这是重点。我想我只是觉得提到这两个答案之间的关系是有意义的,因为它们基于同一个概念。我把这个作为答案,因为这是我处理这个问题的实际方法,但是有很多好的答案,我学到了很多。所以,感谢所有的答案,伙计们:)
data A =
  A {fieldB :: B, fieldC :: C} |
  Aa {fieldB :: B, fieldX1 :: X, fieldX2 :: X} |
  Ag {fieldB :: B, fieldR :: R, fieldQ :: Q} |
  Ax {fieldX :: X}

getB a = case a of
  Ax -> somethingElse
  _  -> fieldB a