避免命名空间污染在Haskell

问题描述 投票:53回答:5

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

data Customer = Customer { ..., foo :: Int, ... }
data Product = Product { ..., foo :: Int, ... }

现在,作为存取“foo”函数被定义了两次,我得到了“多次声明”的错误。为了避免这种情况的一个方法是使用进口的完全限定不同的模块,或者干脆重命名字段(我不想做)。

什么是正式提出这个在Haskell处理方式?

haskell namespaces types records
5个回答
23
投票

这是一个非常毛茸茸的问题。有固定的备案制度若干建议。在一个相关的说明,请参阅TDNRrelated 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问题之前。


12
投票

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

解决方案:

1)前缀字段具有指示类型标签(极为常见)

data Customer = Customer {..., cFoo :: Int, ...}

2)使用类型类(较少见,有人抱怨像cFoo前缀是不方便,但显然并没有那么糟糕,他们会写一个类和实例或使用TH这样做)。

class getFoo a where
    foo :: a -> Int

instance getFoo Customer where
    foo = cFoo

3)使用更好的字段名。如果场实际上是不同的(这并不总是正确的,我的电脑有一样我的员工的年龄),那么这是最好的解决办法。


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

同样,你的目的,我硬着头皮只是重命名字段。但是,这些提法表明,当你真正需要的可扩展记录的全部力量,有办法做到这一点,即使没有一个是愉快的一个精心设计的语言扩展会。


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

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' <.> "]"
© www.soinside.com 2019 - 2024. All rights reserved.