我在程序中使用了很多不同的记录,其中一些使用相同的字段名称,例如
data Customer = Customer { ..., foo :: Int, ... }
data Product = Product { ..., foo :: Int, ... }
现在由于访问器函数“foo”被定义了两次,我得到“多个声明”错误。避免这种情况的一种方法是使用完全限定导入的不同模块,或者只是重命名字段(我不想这样做)。
在Haskell中正式建议的处理方法是什么?
答案 0 :(得分:23)
这是一个非常毛病的问题。 修复记录系统有几个建议。在相关说明中,请参阅TDNR和related discussion on cafe。
使用当前可用的语言功能,我认为最好的选择是在两个不同的模块中定义两种类型,并进行合格的导入。除此之外,如果需要,您可以实现一些类型类机制。
在Customer.hs
module Customer where
data Customer = Customer { ..., foo :: Int, ... }
在Product.hs中
module Product where
data Product = Product { ..., foo :: Int, ... }
使用它们时,在Third.hs中
module Third where
import qualified Customer as C
import qualified Product as P
.. C.foo ..
.. P.foo ..
然而,我想在你遇到关于recursively dependent modules的问题之前不会太迟。
答案 1 :(得分:12)
(仅供参考,这个问题几乎肯定是重复的)
解决方案:
1)使用指示类型(非常常见)的标签前缀字段
data Customer = Customer {..., cFoo :: Int, ...}
2)使用类型类(不太常见,像cFoo
这样的人抱怨前缀不方便,但显然不是很糟糕,以至于他们会写一个类和实例,或者使用TH来做同样的事情。)
class getFoo a where
foo :: a -> Int
instance getFoo Customer where
foo = cFoo
3)使用更好的字段名称 如果字段实际上不同(这并非总是如此,我的计算机的年龄与我的员工一样),那么这是最佳解决方案。
答案 2 :(得分:7)
另请参阅包装: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
再次,为了您的目的,我会咬紧牙关,只是重命名字段。但是这些参考文献表明,当你真正需要可扩展记录的全部功能时,有很多方法可以做到,即使没有一个方法可以像设计良好的语言扩展那样令人愉快。
答案 3 :(得分: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
答案 4 :(得分:0)
一种可能使您的代码不那么冗长的解决方案是将<.>
定义为:
(<.>) :: (Emiter e1, Emiter e2) => e1 -> e2 -> String
lhs <.> rhs = emit lhs <> emit rhs
然后发射器看起来像:
class Emiter n where
emit :: n -> String
instance Emiter String where
emit = id
instance Emiter A where
emit A {
foo = foo'
bar = bar'
} = foo' <.> "--" <.> bar'
instance Emiter B where
emit B {
foo = foo'
bar = bar'
} = "[" <.> bar' <.> foo' <.> "]"