Haskell没有记录成员的点表示法。对于每个记录成员,编译器创建一个具有相同名称的函数,其类型为RecType - >的FieldType。这会导致名称冲突。有没有办法解决这个问题,即我怎么能有几个具有相同字段名称的记录?
答案 0 :(得分:19)
对于大型项目,我更喜欢将每种类型保留在自己的模块中,并使用Haskell的模块系统来为每种类型命名空间访问器。
例如,我可能在模块A
中有一些类型A
:
-- A.hs
data A = A
{ field1 :: String
, field2 :: Double
}
...以及模块B
中具有类似命名字段的另一种类型B
:
-- B.hs
data B = B
{ field1 :: Char
, field2 :: Int
}
然后,如果我想在其他模块C
中使用这两种类型,我可以导入它们,以区分我的意思是哪个访问者:
-- C.hs
import A as A
import B as B
f :: A -> B -> (Double, Int)
f a b = (A.field2 a, B.field2 b)
不幸的是,Haskell无法在同一个模块中定义多个名称空间,否则需要在单独的模块中拆分每个类型才能执行此操作。
答案 1 :(得分:17)
避免此问题的另一种方法是使用lens包。它提供了一个makeFields模板haskell函数,你可以这样使用:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeSynonymInstances #-}
import Control.Lens
data A = A
{ _aText :: String
}
makeFields ''A -- Creates a lens x for each record accessor with the name _aX
data B = B
{ _bText :: Int
, _bValue :: Int
}
-- Creates a lens x for each record accessor with the name _bX
makeFields ''B
main = do
let a = A "hello"
let b = B 42 1
-- (^.) is a function of lens which accesses a field (text) of some value (a)
putStrLn $ "Text of a: " ++ a ^. text
putStrLn $ "Text of b: " ++ show (b ^. text)
如果你不想使用TemplateHaskell和镜头,你也可以手动使用TemplateHaskell手动镜头自动化:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
data A = A
{ aText :: String
}
data B = B
{ bText :: Int
, bValue :: Int
}
-- A class for types a that have a "text" field of type t
class HasText a t | a -> t where
-- An accessor for the text value
text :: a -> t
-- Make our two types instances of those
instance HasText A String where text = aText
instance HasText B Int where text = bText
main = do
let a = A "hello"
let b = B 42 1
putStrLn $ "Text of a: " ++ text a
putStrLn $ "Text of b: " ++ show (text b)
但我真的可以推荐学习镜头,因为它还提供了许多其他工具,例如修改或设置字段。
答案 2 :(得分:6)
请注意,GHC开发人员似乎计划在未来如何处理此问题。查看我在this plan末尾提到的this blog post。