5

我正在检查当前没有模板-haskell/一些镜头幻想的做法来处理以下简单情况:

data Person = Person {
  name :: String,
  ...
}

data Company = Company {
  name :: String,
  ...
}

目前,我通过限定导入来避免污染全局命名空间,但它确实使记录访问变得笨拙。

import Person as P

isFred :: Person -> Bool
isFred p = (P.name p) == "Fred"

真的没有更好的方法来访问记录字段吗?


我接受@Emmanuel Touzery 的回答,因为有用的链接指向另一个涵盖相同领域的问题。另一个问题没有出现在“haskell 命名空间”的搜索中。其他答案没有错,但我只能接受一个。

那里提到的解决方案使用模板 Haskell、镜头、类型类等,基本上为每个字段“HasName”创建一个类型类,并使用单个函数“名称”。然后,每个数据类型都是该类的一个实例,具有自己的实现。然后有一些我不完全理解的魔法允许涉及不同的类型。

对于任何想知道这是怎么回事的 Haskell 新手来说,这是因为记录基本上是元组,其字段选择器实现为普通函数,用于选择(例如)该元组的第二个元素。如果您导出这些字段选择器函数,那么它们位于全局命名空间中,迟早(通常更早)您会遇到冲突。

所以 - 你要么限定导入(如我上面的示例),要么尝试提出不冲突的名称(前缀名称并希望最好)。

镜头的东西在 2013 年风靡一时,允许组合字段选择器/吸气剂 + 设置器等。镜头的基本概念并不太复杂,但实现就在我头上。


为了记录(哈!)我认为另一篇文章中的解决方案可能是我所追求的,但它确实涉及大量的魔法(5 个扩展只是为了伪造记录命名空间)。

4

3 回答 3

7

通常只有两种方法,但不幸的是,社区中没有关于它们的共识:

  1. 将带有针对它们的功能的记录放在单独的文件中,并使用完整别名限定它们,如下所示:

    import qualified Data.Person as Person; import Data.Person (Person)
    
    isFred :: Person -> Bool
    isFred p = (Person.name p) == "Fred"
    

    考虑这种方法与 Java 等语言中的方法相同,其中文件仅包含一个类。

  2. 将您的记录放在同一个文件中,同时在字段名称之前加上记录名称,例如:

    data Person = Person {
      personName :: String,
      personAge :: Int,
      ...
    }
    

这两个镜头库都没有解决这个问题。

于 2013-10-04T08:48:17.107 回答
2

已经提出了类似的问题,您可以在此处看到建议镜头的答案: https ://stackoverflow.com/a/17488365/516188

现在镜头是 Haskell 社区的一个流行词,它们肯定有它们的用途,而且它们可能是长期解决这个命名空间问题的一部分。但我认为目前仅使用镜头来解决这个问题的人将是少数。正如 Nikita Volkov 所说,合格的导入和前缀将是此时的典型解决方案。

更新:发现了这个尚未最终确定但似乎很有前途的另一个选项。在这篇博文的末尾提到了这里。

于 2013-10-04T13:10:40.117 回答
0

这是我的方法。在实践中,我发现我并不经常需要导出记录名称,因此命名空间污染只是模块本身的一个问题。所以我使用了一个短前缀,通常是类型类的第一个字母,如下所示:

data Person = Person {
  pName :: String,
  ...
}

data Company = Company {
  cName :: String,
  ...
}

在我确实需要允许其他模块直接访问一个字段的情况下,它通常只有一个或两个字段。通常我只想允许读取访问。所以在那种情况下,我可能有点创意,也许是这样的。

module Whatever
  {
     Person,
     personName,
     Company,
     companyName,
     ...
   }

data Person = Person {
  pName :: String,
  ...
}

data Company = Company {
  cName :: String,
  ...
}

personName :: Person -> String
personName = pName

companyName :: Company -> String
companyName = cName
于 2013-10-04T09:55:41.870 回答