【问题标题】:Name clashes between field labels of different datatypes in HaskellHaskell中不同数据类型的字段标签之间的名称冲突
【发布时间】:2010-07-19 00:47:43
【问题描述】:

从各种 OO 语言的背景来到 Haskell,对我来说似乎有点缺点的一件事是函数和字段名称的范围不限于它们所关联的类型,因此它很容易运行如果不同的数据类型具有相同名称的字段,则会发生冲突。

如果我有这三个模块:

module One where

data Foo a = Foo { value :: a }

----

module Two where

data Bar a = Bar { value :: a }

----

module Three where

import One
import Two

foo = Foo { value = 42 }  -- compile error here
n = value foo  -- and here

模块Three 中对value 的非限定引用被认为是模棱两可的,即使两个导入的名称中只有一个在这种情况下有意义。 (在 OO 语言中,对 foo.valuebar.value 的引用是明确的。)

当然,我可以写Foo { One.value = 42 } 来消除歧义,但这看起来很尴尬。我也可以以不同的方式命名字段,例如“fooValue”和“barValue”,但Foo { fooValue = 42 } 中的冗余看起来也很尴尬。

这实际上是一个特殊情况,即不同模块中的函数具有相同的名称但在不同的、不相关的类型上运行的更普遍的问题。不过,我似乎更经常遇到字段名称。例如,我有几个数据类型,它们与类型类无关,但经常一起使用,它们包含颜色值,所以我希望每个数据类型都有一个名为“color”的字段。

有经验的 Haskell 开发人员如何命名事物并将它们组织成模块,以避免这种情况?

【问题讨论】:

  • 次要问题:您在 Bar 的定义中缺少构造函数。
  • @Travis Brown:哎呀,这是一个错字。感谢您指出。

标签: haskell syntax


【解决方案1】:

GHC 扩展 -XDisambiguateRecordFields 将允许 foo = Foo { value = 42 }(但不允许 n = value foo)。

large body of literature 关于 Haskell 当前记录系统的缺点和替代它的候选者,以及一些试图提供更好解决方案的库。 fclabels是我个人用过的唯一一个。

This StackOverflow question 类似,其中的一些答案可能对您也有用。

这确实是一个特例 更一般的功能问题 不同的模块具有相同的 名称但操作不同, 不相关的类型。

Qualified imports 和别名通常足以解决这个问题。

【讨论】:

    【解决方案2】:

    有经验的 Haskell 开发人员如何命名事物并将它们组织成模块,以避免这种情况?

    我只与一些有经验的 Haskell 开发人员合作过,他们做的事情很糟糕,比如

    data Foo a = Foo { foo_value :: a }
    
    data Bar a = Bar { bar_value :: a }
    

    甚至

    data Apocalypse a = A { ap_value :: a }
    

    总的来说,我有一种感觉,很多旧时的 Haskeller 不喜欢合格的名字,真的想假装世界只有一个大名字空间,直接从黑暗时代出来。 (曾经有一段时间,C 编译器对字段名称有相同的限制,这就是为什么 struct stat 中的模式称为 st_mode 而不仅仅是普通的 mode。)

    您可以使用类型类重载名称,但我认识的有经验的开发人员不喜欢无缘无故的类型类。我永远无法弄清楚他们什么时候认为类型课程是免费的。

    我希望有朝一日 Haskell 人会接受分层命名空间并开始使用限定名称。正如上帝的旨意。

    【讨论】:

    • 当您实际上并不想要多态记录选择器时,我认为不要仅仅为了一些小的语法便利而强制它们。即使您可以使用合格的东西,但在我看来,为一个很少使用的字段(尤其是通常只能通过辅助函数访问的字段)声明一个通用名称是不好的形式。
    • 我一直在尽可能多地使用限定名称。语言本身可以很好地处理它;唯一的问题是 Haddock 对限定名称的用处要小得多,因为它仍然假定一个大名称空间。 (您可以使 Haddock 显示类型名称合格,但仍会显示不合格的超链接。)
    【解决方案3】:

    如果所有这些类型都有一个公共访问器函数,您可能会考虑一个类型类。例如

    class Fieldable a where
         field :: a -> b
    
    instance Fieldable (a,b) where
         field = fst
    

    等等

    【讨论】:

    • 数据类型的字段名称可以是类实例的一部分吗?如果我创建一个class Valued a b where value :: a -> b 然后将我的两种类型与它相关联,我将不得不做类似instance Valued (Foo a) a where value = value 的事情,这是无效的(value 的两种含义冲突)。如果事先已经定义了函数的名称,我似乎无法将函数定义为类实例的一部分。
    猜你喜欢
    • 1970-01-01
    • 2013-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    相关资源
    最近更新 更多