哈斯克尔 - 任何方式来限定或消除记录名称的歧义? [英] Haskell -- any way to qualify or disambiguate record names?

查看:134
本文介绍了哈斯克尔 - 任何方式来限定或消除记录名称的歧义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两种数据类型,用于 hastache 模板。我的代码中有两种不同的类型,都有一个名为name的字段。这当然会导致冲突。似乎有一种机制可以消除对名称的任何调用,但实际的定义会导致问题。是否有任何解决方法,说让记录字段名称限定?

  data DeviceArray = DeviceArray 
{name ::字符串,
字节:: Int}
派生(Eq,Show,Data,Typeable)

data TemplateParams = TemplateParams
{arrays :: [DeviceArray],
input :: DeviceArray}
导出(Eq,Show,Data,Typeable)

数据MakefileParams = MakefileParams
{名称:: String}
deriving Eq,Show,Data,Typeable)

即如果这些字段现在在代码中使用,它们将是DeviceArray.name和MakefileParams.name?

解决方案

已经注意到,这不是直接可能的,但我想说一些关于提议的解决方案的事情:



如果两个字段明显不同,你总是想知道你在用哪个。在这里,明显不同的意思是说,永远不会有这样一种情况,即在任何一个领域做同样的事情都是有意义的。鉴于此,过度的歧义并不是真正的不受欢迎,所以如果这更符合您的口味,您会希望将符合条件的进口作为标准方法或字段消歧扩展名。或者,作为一个非常简单(并且稍微难看)的选项,只需手动为字段添加前缀。 deviceArrayName 而不是 name



领域在某种意义上是相同的东西,能够以均匀的方式对待它们是有意义的;理想情况下,你可以在 name 字段中选择一个多态函数。在这种情况下,有一个选项是使用 type class 作为命名事物,其功能允许您以任何适当类型访问名称字段。这里的一个主要缺点是,除了可怕的类型约束和可怕的单形态约束可能令人头疼之外,你还失去了使用记录语法的能力,这开始打败了整个观点。



类似字段的另一个主要选项是我提取的名称字段输出为单个参数化类型,例如 data命名为a =命名为{name :: String,item :: a} GHC本身使用这种方法来源位置语法树,虽然它不使用记录语法,但这个想法是相同的。这里的缺点是,如果你有一个名为DeviceArray ,访问字节字段现在需要经过两层记录。如果你想用函数更新字节字段,你会遇到这样的问题:



<$ p $ (item na){bytes = b + bytes(item na)}}

呃。有一些方法可以缓解这个问题,但在我看来,他们仍然不知道。像这样的例子就是为什么我不喜欢一般的记录语法。所以,作为最后的选项,一些模板Haskell 魔术和
$ b

 b 
导入Control.Category
导入Data.Record.Label

data命名a =命名
{_name :: String,
_namedItem :: a
导出(Eq,Show,Data,Typeable)

数据DeviceArray = DeviceArray {_bytes :: Int}
导出(Eq,Show,Data,Typeable)

数据MakefileParams = MakefileParams {_makefileParams :: [MakeParam]}
派生(Eq,Show,Data,Typeable)

数据MakeParam = MakeParam {paramText :: String}
导出(Eq,Show,Data,Typeable)
$ b $(mkLabels [''命名,'DeviceArray','MakefileParams','MakeParam])

不介意 MakeParam 业务,我只需要一个字段在那里做一点事用。无论如何,现在你可以修改这样的字段:

  addBytes b = modL(namedItem>>> bytes)(b +)
nubParams = modL(namedItem>>> makefileParams)nub

也可以命名 bytes 类似于 bytesInternal ,然后导出访问器 bytes = namedItem>> ;> bytesInternal ,如果你喜欢。


I have two data types, which are used for hastache templates. It makes sense in my code to have two different types, both with a field named "name". This, of course, causes a conflict. It seems that there's a mechanism to disambiguate any calls to "name", but the actual definition causes problems. Is there any workaround, say letting the record field name be qualified?

data DeviceArray = DeviceArray
    { name :: String,
      bytes :: Int }
    deriving (Eq, Show, Data, Typeable)

data TemplateParams = TemplateParams
    { arrays :: [DeviceArray],
      input :: DeviceArray }
    deriving (Eq, Show, Data, Typeable)

data MakefileParams = MakefileParams
    { name :: String }
    deriving (Eq, Show, Data, Typeable)

i.e. if the fields are now used in code, they will be "DeviceArray.name" and "MakefileParams.name"?

解决方案

As already noted, this isn't directly possible, but I'd like to say a couple things about proposed solutions:

If the two fields are clearly distinct, you'll want to always know which you're using anyway. By "clearly distinct" here I mean that there would never be a circumstance where it would make sense to do the same thing with either field. Given this, excess disambiguity isn't really unwelcome, so you'd want either qualified imports as the standard approach, or the field disambiguation extension if that's more to your taste. Or, as a very simplistic (and slightly ugly) option, just manually prefix the fields, e.g. deviceArrayName instead of just name.

If the two fields are in some sense the same thing, it makes sense to be able to treat them in a homogeneous way; ideally you could write a function polymorphic in choice of name field. In this case, one option is using a type class for "named things", with functions that let you access the name field on any appropriate type. A major downside here, besides a proliferation of trivial type constraints and possible headaches from the Dreaded Monomorphism Restriction, is that you also lose the ability to use the record syntax, which begins to defeat the whole point.

The other major option for similar fields, which I didn't see suggested yet, is to extract the name field out into a single parameterized type, e.g. data Named a = Named { name :: String, item :: a }. GHC itself uses this approach for source locations in syntax trees, and while it doesn't use record syntax the idea is the same. The downside here is that if you have a Named DeviceArray, accessing the bytes field now requires going through two layers of records. If you want to update the bytes field with a function, you're stuck with something like this:

addBytes b na = na { item = (item na) { bytes = b + bytes (item na) } }

Ugh. There are ways to mitigate the issue a bit, but they're still not idea, to my mind. Cases like this are why I don't like record syntax in general. So, as a final option, some Template Haskell magic and the fclabels package:

{-# LANGUAGE TemplateHaskell #-}

import Control.Category
import Data.Record.Label

data Named a = Named 
    { _name :: String, 
      _namedItem :: a }
    deriving (Eq, Show, Data, Typeable)

data DeviceArray = DeviceArray { _bytes :: Int }
    deriving (Eq, Show, Data, Typeable)

data MakefileParams = MakefileParams { _makefileParams :: [MakeParam] }
    deriving (Eq, Show, Data, Typeable)

data MakeParam = MakeParam { paramText :: String }
    deriving (Eq, Show, Data, Typeable)

$(mkLabels [''Named, ''DeviceArray, ''MakefileParams, ''MakeParam])

Don't mind the MakeParam business, I just needed a field on there to do something with. Anyway, now you can modify fields like this:

addBytes b = modL (namedItem >>> bytes) (b +)
nubParams = modL (namedItem >>> makefileParams) nub

You could also name bytes something like bytesInternal and then export an accessor bytes = namedItem >>> bytesInternal if you like.

这篇关于哈斯克尔 - 任何方式来限定或消除记录名称的歧义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆