避免Haskell中的命名空间污染

54 haskell types records namespaces

我在程序中使用了很多不同的记录,其中一些使用相同的字段名称,例如

data Customer = Customer { ..., foo :: Int, ... }
data Product = Product { ..., foo :: Int, ... }
Run Code Online (Sandbox Code Playgroud)

现在,由于访问器函数"foo"被定义了两次,我得到"多个声明"错误.避免这种情况的一种方法是使用完全限定导入的不同模块,或者只是重命名字段(我不想这样做).

在Haskell处理这个问题的官方建议方法是什么?

Ozg*_*gur 23

这是一个非常毛茸茸的问题.有几个修复记录系统的建议.在相关的说明中,请参阅TDNR咖啡馆的相关讨论.

使用当前可用的语言功能,我认为最好的选择是在两个不同的模块中定义两种类型,并进行合格的导入.除此之外,如果需要,您可以实现一些类型类机器.

在Customer.hs中

module Customer where
data Customer = Customer { ..., foo :: Int, ... }
Run Code Online (Sandbox Code Playgroud)

在Product.hs中

module Product where
data Product = Product { ..., foo :: Int, ... }
Run Code Online (Sandbox Code Playgroud)

使用它们时,在Third.hs中

module Third where

import qualified Customer as C
import qualified Product as P

.. C.foo ..
.. P.foo ..
Run Code Online (Sandbox Code Playgroud)

然而,我想你在解决递归依赖模块的问题之前还为时不晚.


Tho*_*son 12

(仅供参考,这个问题几乎肯定是重复的)

解决方案:

1)使用指示类型的标记前缀字段(非常常见)

data Customer = Customer {..., cFoo :: Int, ...}
Run Code Online (Sandbox Code Playgroud)

2)使用类型类(不太常见,人们抱怨前缀像是cFoo不方便但显然不是那么糟糕,他们会写一个类和实例或使用TH来做同样的事情).

class getFoo a where
    foo :: a -> Int

instance getFoo Customer where
    foo = cFoo
Run Code Online (Sandbox Code Playgroud)

3)使用更好的字段名称如果字段实际上不同(这并非总是如此,我的计算机的年龄与我的员工一样),那么这是最佳解决方案.


scl*_*clv 7

另见Has包:http://chrisdone.com/posts/duck-typing-in-haskell

如果您现在确实需要可扩展记录,则可以始终使用HList.但是在你对中等先进的Haskell非常熟悉和熟悉之前我不会推荐这个,即便如此,我还是会检查你是否需要它.

Haskelldb的版本稍微轻一些:http://hackage.haskell.org/packages/archive/haskelldb/2.1.0/doc/html/Database-HaskellDB-HDBRec.html

然后是葡萄柚frp库的另一个版本的可扩展记录:http://hackage.haskell.org/package/grapefruit-records

再次,为了您的目的,我会咬紧牙关,只是重命名字段.但是这些参考文献表明,当你真正需要可扩展记录的全部功能时,有很多方法可以做到,即使没有一个像设计良好的语言扩展那样令人愉快.


unc*_*chu 6

有一种语言扩展DuplicateRecordFields,允许重复字段函数,并使其类型由类型注释推断.

这是一个小例子(haskell-stack脚本):

#!/usr/bin/env stack
-- stack runghc --resolver lts-8.20 --install-ghc

{-# LANGUAGE DuplicateRecordFields #-}

newtype Foo = Foo { baz :: String }
newtype Bar = Bar { baz :: String }

foo = Foo { baz = "foo text" }
bar = Bar { baz = "bar text" }

main = do
  putStrLn $ "Foo: " ++ baz (foo :: Foo) -- Foo: foo text
  putStrLn $ "Bar: " ++ baz (bar :: Bar) -- Bar: bar text
Run Code Online (Sandbox Code Playgroud)