F#:将受歧视的联合和类层次结构结合在一起?
假设我有一个重要的类层次结构:
Tag
ControlFlowTag
IfTag
ForTag
JumpTag
HTMLTag
DivTag
并且我想制作一个散布这些和字符串的列表。
let MyList = [tagA, tagB, "some text", tagC]
我以为我可以区分联合它,
type Node =
| Tag of Tag
| String of String
let MyList: list<Node> = [tagA, tagB, "some text", tagC]
但可惜的是,如果没有它,它就无法工作
let MyList: list<Node> = [Tag tagA, Tag tagB, String "some text", Tag tagC]
。 显然,Node 中描述的 Tag 和 String 是正交的,并且与现有的 Tag/String 类分开。将鼠标悬停给我的类型为 Node.Tag
和 Node.String
,这不是我想要的。
我现在拥有的是一个函数t
,它创建了一个继承自Tag
的StringTag
,这给了我
let MyList : list<Tag> = [tagA, tagB, t"some text", tagC]
相当不错的功能,但是额外的t
增加了视觉噪音。我真正想要的是一个强类型的“两种不同类型的列表”,我可以使用 match
语句来使用它。我认为这就是受歧视联合的要点,但它们无法使用现有的类型层次结构是一个问题,因为现有的层次结构(在本例中为 Tag
)足够复杂,我认为完整的 OO 继承方法类型子集比纯粹的可区分联合方法更清晰
一种选择是仅将其设为 obj 列表并在 match
之前/期间强制转换所有内容,但这不是真的非常 好的。还有其他方法吗?
Let's say I have a significant class hierarchy:
Tag
ControlFlowTag
IfTag
ForTag
JumpTag
HTMLTag
DivTag
and I want to make a list interspersed with these and strings.
let MyList = [tagA, tagB, "some text", tagC]
and I thought I could discriminated union it
type Node =
| Tag of Tag
| String of String
let MyList: list<Node> = [tagA, tagB, "some text", tagC]
but alas, it doesn't work without
let MyList: list<Node> = [Tag tagA, Tag tagB, String "some text", Tag tagC]
Obviously the Tag and String described in Node are orthogonal and separate from the existing Tag/String classes. Mousing over gives me the types as Node.Tag
and Node.String
, which isn't what I want.
What I have now is a function t
which creates a StringTag
which inherits from Tag
, giving me
let MyList : list<Tag> = [tagA, tagB, t"some text", tagC]
which is pretty nice, but the extra t
adds to the visual noise. What I actually want is a strongly typed "list of two different types" which I could work with using match
statements. I thought that was the point of Discriminated Unions, but their inability to use existing type hierarchies is a problem, since the existing hierarchy (in this case Tag
) is complex enough I think a full OO-inheritence approach to that subset of types is clearer than a pure Discriminated Union approach
One option is to just make it a list of obj
and cast everything before/during the match
, but that's not really very nice. Are there any other approaches?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您有两个不同的 DU,那么
编译
器如何知道以下列表属于哪种类型?
正如斯维克所说,鉴别器是必要的。如果您使用类,则需要向上转换为基本类型,因此我不确定是否可以节省击键次数。
如果您正在使用字典,这里是一个不错的选择减少拳击的语法噪音。也许你可以对列表做类似的事情。
If you had two different DUs, say
and
how would the compiler know of which type the following list is?
As svick said, the discriminator is necessary. If you use classes instead you'll need to upcast to the base type, so I'm not sure you save on keystrokes.
If you're working with dictionaries, here is a nice option to reduce the syntactic noise of boxing. Maybe you can do something similar for lists.
我不知道这有多大帮助,但如果合适的话,您可以使用活动模式以类似 DU 的方式匹配类层次结构。
I don't know how helpful this is, but you can use Active Patterns to match a class hierarchy in a DU-like fashion if appropriate.
受歧视的工会就是受歧视的(与 C 工会不同)。这意味着您必须始终添加鉴别器。
如果这是 C#,我会考虑从
string
到StringTag
的隐式转换。但由于 F# 不支持隐式转换,我认为第二种方法是最好的选择。尽管我会让函数的名称更具描述性,而不仅仅是t
。大多数时候,最好编写易于阅读的代码,而不是易于编写的代码。Discriminated unions are just that – discriminated (unlike e.g. C unions). That means you have to always add the discriminator.
If this were C#, I would think about having an implicit conversion from
string
toStringTag
. But since F# doesn't support implicit conversions, I think the second approach is your best bet. Although I would make the function's name more descriptive, not justt
. Most of the time, it's better to write code that's easy to read, not code that's easy to write.