简体   繁体   English

Haskell 中的嵌套“where”或“let in”CodeStyle

[英]Nested "where" or "let in" CodeStyle in Haskell

I faced with some issue in Haskell: code-style and big functions (I'm continuing to learn Haskell through writing toy-language).我在 Haskell 中遇到了一些问题:代码风格和大功能(我通过编写玩具语言继续学习 Haskell)。

I have some necessary-big-function (see example).我有一些必要的大功能(参见示例)。 And there are two sub-function (nested where) in it.其中有两个子功能(嵌套在哪里)。 And there is no reason to place its sub-finction in module scoupe.并且没有理由将其子功能放在模块 scope 中。 How "Haskell code-style" or "Haskell code-style best practics" suggest to solve this problem of "ungraceful and clumsy code"? “Haskell 代码风格”或“Haskell 代码风格最佳实践”如何建议解决“不优雅和笨拙的代码”问题?

Function (with later comment): Function(后面有评论):

-- We can delete (on DCE) any useless opers.
-- Useful opers - only opers, whitch determine (directly or transitivery) result of GlobalUse oper
addGlobalUsageStop :: [Var] -> IR -> IR
addGlobalUsageStop guses iR = set iOpers (ios ++ ios') $ set opN opn' iR
    where
    ios = _iOpers iR
    gdefs = _gDefs iR :: M.Map Int Var
    opn = _opN iR
    guses' = nub $ filter isRegGlobal guses
    ogs = catMaybes $ map (concatIOperWithGDef gdefs) $ reverse ios
        where
        concatIOperWithGDef gdefs' (i, o) = case gdefs' M.!? i of
            Nothing -> Nothing
            Just gd -> Just (o, gd)
    nops = newGUses ogs guses'
        where
        newGUses [] _ = []
        newGUses _ [] = []
        newGUses ((Oper _ d _ _, g):os) guses = if elem g guses
            then (Oper GlobalUse g (RVar d) None):newGUses os (filter (g /=) guses)
            else newGUses os guses
    ios' = zip [opn..] nops
    opn' = opn + length ios'  

Notices:注意事项:

  1. If you want to know why I even wrote such big function the answer is: because this is some big (and one-needed functionality in compiler): - for each "returning variable" we shoul find last operation, witch defines it (actually corresponding virtual register), and expand our IR with constructed opers.如果你想知道为什么我什至写了这么大的 function 答案是:因为这是一些大的(编译器中需要的功能): - 对于每个“返回变量”,我们应该找到最后一个操作,女巫定义它(实际上对应虚拟寄存器),并使用构造的操作扩展我们的 IR。

  2. I'v seen some similar questions: Haskell nested where clause and "let... in" syntax but they are about "how typewrite correct code?", and my question "is this code Code-Style correct, and if it isn't - what should i do?".我见过一些类似的问题: Haskell 嵌套了 where 子句和“let... in”语法,但它们是关于“如何键入正确的代码?”,而我的问题是“这个代码代码样式是否正确,如果不是” t - 我该怎么办?”。

And there is no reason to place its sub-function in module scope并且没有理由将其子功能放在模块scope中

Think about it the other way around.反过来想。 Is there any reason to put the sub-function in the local scope?有什么理由把子功能放在本地scope中? Then do it.然后去做。 This could be because这可能是因为

  1. it needs to access locally bound variables.它需要访问本地绑定的变量。 In this case it must be local, or else you need extra parameters.在这种情况下,它必须是本地的,否则您需要额外的参数。
  2. it does something very obvious and only relevant to the specific use case.它做了一些非常明显且仅与特定用例相关的事情。 This could be one-line definitions of some operation that you don't care thinking a properly descriptive name, or it could be a go helper that does basically the whole work of the enclosing function.这可能是某些操作的单行定义,您不需要考虑正确的描述性名称,也可能是go助手,它基本上完成了封闭 function 的全部工作。

If neither of these apply, and you can give the local function a descriptive name (as you've already done) then put it in module scope.如果这些都不适用,您可以给本地 function 一个描述性名称(正如您已经完成的那样),然后将其放入模块 scope 中。 Add a type signature, which makes it clearer yet.添加类型签名,使其更清晰。 Don't export it from the module.不要从模块中导出它。

Putting the function in module scope also removes the need to rename variables like you did with gdefs' .将 function 放入模块 scope 也无需像使用gdefs'那样重命名变量。 That's one of the more common causes for bugs in Haskell code.这是 Haskell 代码中的错误的更常见原因之一。

The question is a good one, but the example code isn't a great example.这个问题很好,但示例代码不是一个很好的例子。 For me, the correct fix in this particular case is not to talk about how to stylishly nest where s;对我来说,在这种特殊情况下的正确解决方法是不要谈论如何时尚地嵌套where s; it's to talk about how to use library functions and language features to simplify the code enough that you don't need where in the first place.它是谈论如何使用库函数和语言特性来简化代码,以至于你不需要首先where In particular, list comprehensions get you very far here.特别是,列表推导可以让你走得很远。 Here's how I would write those two definitions:以下是我将如何编写这两个定义:

import Data.Containers.ListUtils (nubOrdOn)

... where
    ogs = [(o, gd) | (i, o) <- reverse ios, Just gd <- [gdefs M.!? i]]
    nops = nubOrdOn fun
        [ Oper GlobalUse g (RVar d) None
        | (Oper _ d _ _, g) <- ogs
        , g `elem` guses'
        ]
    fun (Oper _ g _ _) = g -- this seems useful enough to put at the global scope; it may even already exist there

Since ogs isn't mentioned anywhere else in the code, you might consider inlining it:由于代码中的其他任何地方都没有提到ogs ,因此您可以考虑将其内联:

    -- delete the definition of ogs
    nops = nubOrdOn fun
        [ Oper GlobalUse g (RVar d) None
        | (i, Oper _ d _ _) <- reverse ios
        , Just g <- [gdefs M.!? i]
        , g `elem` guses'
        ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM