在 Haskell 中嵌套 "where" 或 "let in" CodeStyle

Nested "where" or "let in" CodeStyle in Haskell

我在 Haskell 中遇到了一些问题:代码风格和大函数(我正在通过编写玩具语言继续学习 Haskell)。

我有一些必要的大功能(见示例)。其中有两个子功能(嵌套在何处)。并且没有理由将其子功能放在模块 scoupe 中。 “Haskell code-style”或“Haskell code-style best practics”如何建议解决这个“不优雅和笨拙的代码”的问题?

函数(稍后评论):

-- 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'  

通知:

  1. 如果你想知道我为什么要写这么大的函数,答案是: 因为这是一些大的(编译器中唯一需要的功能): - 对于每个“返回变量”,我们应该找到最后一个操作, 定义它(实际上对应的虚拟寄存器),并用构造的操作扩展我们的 IR。

  2. 我看过一些类似的问题: 但它们是关于“如何输入正确的代码?”,以及我的问题“这段代码的代码风格是否正确,如果不正确 - 我应该怎么做?”。

And there is no reason to place its sub-function in module scope

反过来想一想。是否有任何理由将子功能放在本地范围内?然后去做。这可能是因为

  1. 它需要访问本地绑定变量。在这种情况下,它 必须 是本地的,否则你需要额外的参数。
  2. 它做了一些非常明显并且只与特定用例相关的事情。这可能是一些操作的单行定义,您不在乎考虑一个适当的描述性名称,或者它可能是一个 go 助手,它基本上完成了封闭函数的全部工作。

如果这些都不适用,并且您可以给本地函数一个描述性名称(正如您已经完成的那样),然后将其放入模块范围。添加类型签名,使其更清晰。不要从模块中导出它。

将函数放在模块范围内也无需像在 gdefs' 中那样重命名变量。这是 Haskell 代码中错误的更常见原因之一。

这个问题很好,但示例代码不是很好的例子。对我来说,在这种特殊情况下 的正确解决方法不是谈论如何时尚地嵌套 where;它是在讨论如何使用库函数和语言特性来简化代码,使您一开始就不需要 where。特别是,列表推导式可以让您走得更远。下面是我将如何编写这两个定义:

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

由于代码中其他地方未提及 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'
        ]