在 Haskell 中的 case 构造中使用匹配值

发布于 2024-12-11 09:33:12 字数 613 浏览 1 评论 0原文

我正在记录的数据构造函数上进行模式匹配,并且有以下代码块:

colorFor shape = 
  case material shape of
    ColorMaterial -> material shape
    -- etc.

问题是这样的: material 是一个不平凡的方法,我不想在中重新计算它案件陈述。我知道我可以做类似的事情:

colorFor shape = 
  let m = material shape
  in case m of
    ColorMaterial -> m

或者

colorFor shape = 
  case material shape of
    ColorMaterial r g b -> ColorMaterial r g b

但我忍不住认为必须有某种方法来检索模式匹配中的匹配值。这也出现在函数定义中,我想在数据构造函数上匹配某些参数,而不需要完全解压它。

仅供参考:我是 Haskell 的新手,如果有更好的方法来做我正在做的事情,我非常愿意接受建议。非常感谢任何帮助!

I'm pattern matching on the data constructor of a record, and I have the following chunk of code:

colorFor shape = 
  case material shape of
    ColorMaterial -> material shape
    -- etc.

The problem is this: material is a nontrivial method, and I'd like to not recompute it in the case statement. I know I could do something like:

colorFor shape = 
  let m = material shape
  in case m of
    ColorMaterial -> m

or

colorFor shape = 
  case material shape of
    ColorMaterial r g b -> ColorMaterial r g b

But I couldn't help but think there must be some way to retrieve the matched value in a pattern match. This also comes up for function definitions, where I'd like to match on the data constructor for some argument without completely unpacking it.

FYI: I'm new to Haskell, and if there are better ways of doing what I'm doing I'm very open to suggestion. Any help greatly appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

年少掌心 2024-12-18 09:33:12

您正在寻找“作为模式”:

data SomeDataType = ColorMaterial Int Int Int
                  | BlandMaterial

colorFor shape = 
  case material shape of
    res@(ColorMaterial _ _ _) -> res
    -- etc.

You are looking for "as patterns":

data SomeDataType = ColorMaterial Int Int Int
                  | BlandMaterial

colorFor shape = 
  case material shape of
    res@(ColorMaterial _ _ _) -> res
    -- etc.
无敌元气妹 2024-12-18 09:33:12

我不太确定有什么“更好”的方法可以做到这一点,

colorFor shape =  
  let m = material shape 
  in case m of 
      ColorMaterial -> m 

特别是看看 ghc 对 Thomas 建议运行 ghc -ddump-ds 的“as 模式”做了什么,表明 ghc 只是在脱糖后将其变成了 let

 Main.colorFor :: forall t_ac9. t_ac9 -> Main.SomeDataType
 [LclId]
 Main.colorFor =
   \ (@ t_ac9) ->
     letrec {
       colorFor_ac8 :: t_ac9 -> Main.SomeDataType
       [LclId]
       colorFor_ac8 =
         \ (shape_aby :: t_ac9) ->
           **let** {
             res_abz :: Main.SomeDataType
             [LclId]
             **res_abz** =
               GHC.Err.undefined @ (t_ac9 -> Main.SomeDataType) shape_aby } in
           let {
             fail_dcV :: GHC.Prim.State# GHC.Prim.RealWorld -> Main.SomeDataType
             [LclId]
             fail_dcV =
               \ (ds_dcW :: GHC.Prim.State# GHC.Prim.RealWorld) ->
                 Control.Exception.Base.patError
                   @ Main.SomeDataType "Test.hs:(5,3)-(6,36)|case" } in
           case res_abz of wild_B1 {
             __DEFAULT -> fail_dcV GHC.Prim.realWorld#;
             **Main.ColorMaterial ds_dcS ds_dcT ds_dcU -> res_abz**
           }; } in
     colorFor_ac8

它是相当冗长,但我用 <> 标记了重要部分。未定义当然是因为我的代码中不存在“材料”,而我需要它来编译。因此,只有其中之一时,As 模式才会更好。如果再多的话,你最好自己做一个。恕我直言,打字更少,看起来更好。

这似乎也适用于一般情况:

data SomeDataType = ColorMaterial Int Int Int 
                  | BlandMaterial 
                  | NoMaterial

colorFor shape =  
  case undefined shape of 
    res@(ColorMaterial _ _ _) -> res 
    foo@BlandMaterial         -> foo

只是引入了另一个 let,并且已经比只拥有一个 let 打字更多了。

 Main.colorFor :: forall t_ace. t_ace -> Main.SomeDataType
 [LclId]
 Main.colorFor =
   \ (@ t_ace) ->
     letrec {
       colorFor_acd :: t_ace -> Main.SomeDataType
       [LclId]
       colorFor_acd =
         \ (shape_abz :: t_ace) ->
           let {
             **res_abA** :: Main.SomeDataType
             [LclId]
             **res_abA** =
               GHC.Err.undefined @ (t_ace -> Main.SomeDataType) shape_abz } in
           let {
             fail_dd0 :: GHC.Prim.State# GHC.Prim.RealWorld -> Main.SomeDataType
             [LclId]
             fail_dd0 =
               \ (ds_dd1 :: GHC.Prim.State# GHC.Prim.RealWorld) ->
                 Control.Exception.Base.patError
                   @ Main.SomeDataType "Test.hs:(6,3)-(8,36)|case" } in
           **let** {
             foo_abB :: Main.SomeDataType
             [LclId]
             **foo_abB** = res_abA } in
           case res_abA of wild_B1 {
             __DEFAULT -> fail_dd0 GHC.Prim.realWorld#;
             Main.ColorMaterial ds_dcX ds_dcY ds_dcZ -> res_abA;
             Main.BlandMaterial -> foo_abB
           }; } in
     colorFor_acd

I'm not really sure there's any "better" way to do it than

colorFor shape =  
  let m = material shape 
  in case m of 
      ColorMaterial -> m 

In particular looking at what ghc does with the "as pattern" that Thomas suggested running ghc -ddump-ds shows that ghc just makes this into a let after desugaring

 Main.colorFor :: forall t_ac9. t_ac9 -> Main.SomeDataType
 [LclId]
 Main.colorFor =
   \ (@ t_ac9) ->
     letrec {
       colorFor_ac8 :: t_ac9 -> Main.SomeDataType
       [LclId]
       colorFor_ac8 =
         \ (shape_aby :: t_ac9) ->
           **let** {
             res_abz :: Main.SomeDataType
             [LclId]
             **res_abz** =
               GHC.Err.undefined @ (t_ac9 -> Main.SomeDataType) shape_aby } in
           let {
             fail_dcV :: GHC.Prim.State# GHC.Prim.RealWorld -> Main.SomeDataType
             [LclId]
             fail_dcV =
               \ (ds_dcW :: GHC.Prim.State# GHC.Prim.RealWorld) ->
                 Control.Exception.Base.patError
                   @ Main.SomeDataType "Test.hs:(5,3)-(6,36)|case" } in
           case res_abz of wild_B1 {
             __DEFAULT -> fail_dcV GHC.Prim.realWorld#;
             **Main.ColorMaterial ds_dcS ds_dcT ds_dcU -> res_abz**
           }; } in
     colorFor_ac8

It's rather verbose but i've marked the important parts with <>. The undefined is ofcourse because "material" doesn't exist in my code and i needed it there to compile. So the As-Pattern is only better when there's just one of it. Any more and you're better off making one let yourself. Less typing and looks better imho.

This seems to hold in general too:

data SomeDataType = ColorMaterial Int Int Int 
                  | BlandMaterial 
                  | NoMaterial

colorFor shape =  
  case undefined shape of 
    res@(ColorMaterial _ _ _) -> res 
    foo@BlandMaterial         -> foo

just introduces another let, and is already more typing than just having one let.

 Main.colorFor :: forall t_ace. t_ace -> Main.SomeDataType
 [LclId]
 Main.colorFor =
   \ (@ t_ace) ->
     letrec {
       colorFor_acd :: t_ace -> Main.SomeDataType
       [LclId]
       colorFor_acd =
         \ (shape_abz :: t_ace) ->
           let {
             **res_abA** :: Main.SomeDataType
             [LclId]
             **res_abA** =
               GHC.Err.undefined @ (t_ace -> Main.SomeDataType) shape_abz } in
           let {
             fail_dd0 :: GHC.Prim.State# GHC.Prim.RealWorld -> Main.SomeDataType
             [LclId]
             fail_dd0 =
               \ (ds_dd1 :: GHC.Prim.State# GHC.Prim.RealWorld) ->
                 Control.Exception.Base.patError
                   @ Main.SomeDataType "Test.hs:(6,3)-(8,36)|case" } in
           **let** {
             foo_abB :: Main.SomeDataType
             [LclId]
             **foo_abB** = res_abA } in
           case res_abA of wild_B1 {
             __DEFAULT -> fail_dd0 GHC.Prim.realWorld#;
             Main.ColorMaterial ds_dcX ds_dcY ds_dcZ -> res_abA;
             Main.BlandMaterial -> foo_abB
           }; } in
     colorFor_acd
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文