可空嵌套对象和 Maybe Monad
我正在努力实现一个可能的 monad - 在这个例子中我将其称为 Nullable 。
Nullable 类的实现如下:
Public NotInheritable Class Nullable(Of TClass)
Private _value As TClass
Private _hasValue As Boolean
Public Shared Function Create(ByVal value As TClass) As Nullable(Of TClass)
Return New Nullable(Of TClass)(value)
End Function
Public Shared Function Create() As Nullable(Of TClass)
Return New Nullable(Of TClass)()
End Function
Private Sub New()
HasValue = False
End Sub
Private Sub New(theValue As TClass)
Value = theValue
HasValue = True
End Sub
Public Property Value() As TClass
Get
Return _value
End Get
Private Set(value As TClass)
_value = value
End Set
End Property
Public Property HasValue() As Boolean
Get
Return _hasValue
End Get
Private Set(value As Boolean)
_hasValue = value
End Set
End Property
End Class
有一个父类实现为
Class Parent
Public Property ChildClass as Nullable(Of Child)
End Class
,而子类只是
Class Child
Public Property ID as String
... other properties below ...
End Class
monad 的实现方式,目前我需要发出以下命令来访问嵌套子类上的属性
dim id = MyParentClass.ChildClass.Value.ID
,但理想情况下我是这样的想要做的是拥有以下语句
dim id = MyParentClass.ChildClass.Id
,如果 ChildClass 为 null,则仅返回属性类型的默认值。
我尝试使用默认属性来实现此功能,并通过属性将值设置为默认值,但它无法编译。
这是否可能,或者也许有更好的构建它的方法 - 或者也许我只是还没有“得到”可能的单子?
许多 TIA
Simon
I'm struggling to implement a maybe monad - which I've called Nullable in this example.
The Nullable Class is implemented as follows:
Public NotInheritable Class Nullable(Of TClass)
Private _value As TClass
Private _hasValue As Boolean
Public Shared Function Create(ByVal value As TClass) As Nullable(Of TClass)
Return New Nullable(Of TClass)(value)
End Function
Public Shared Function Create() As Nullable(Of TClass)
Return New Nullable(Of TClass)()
End Function
Private Sub New()
HasValue = False
End Sub
Private Sub New(theValue As TClass)
Value = theValue
HasValue = True
End Sub
Public Property Value() As TClass
Get
Return _value
End Get
Private Set(value As TClass)
_value = value
End Set
End Property
Public Property HasValue() As Boolean
Get
Return _hasValue
End Get
Private Set(value As Boolean)
_hasValue = value
End Set
End Property
End Class
There is a parent Class implemented as
Class Parent
Public Property ChildClass as Nullable(Of Child)
End Class
and a Child Class is simply
Class Child
Public Property ID as String
... other properties below ...
End Class
The way the monad is implemented at the moment I would need to issue the following to access a property on the nested child class
dim id = MyParentClass.ChildClass.Value.ID
but ideally what I'd like to be able to do is to have the following statement
dim id = MyParentClass.ChildClass.Id
and if the ChildClass is null then just return a default value for the property type.
I tried implementing this using Default Properties and setting the Value as default via an attribute but it wouldn't compile.
Is that going to be possible or perhaps there is a better way of architecting it - or maybe I just haven't 'got' the maybe monad?
Many TIAs
Simon
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,
Maybe
本身的定义只是一个可能存在也可能不存在的单个值,所以这么多就可以了。顺便说一句,如果您出于某种原因不喜欢“Maybe”这个名称,我建议考虑使用“Option”而不是“Nullable”,因为Nullable
已被 .NET 使用(对于相同的情况)目的,甚至 - 但仅限于值类型),而Option
是 F# 所称的,并且一致性很好。不管怎样,基本上你可以用这样的值做两件事——对值应用转换(如果有的话),并折叠嵌套层(例如,将可空的“foo”变成可空的“foo”) “foo”)。考虑到这些以及构建新价值观的能力,你可以做其他一切事情。
“转换”操作最直接的实现方式是采用 lambda(比较 LINQ 使用的
Select
方法 - 这是同一件事),然后将其应用于包装的值,或者如果存在则将其丢弃没有价值。 “折叠”操作非常简单;如果存在的话,只保留内在价值。 LINQ 在这里是一种非常自然的方法——您可以将Maybe
视为表示最多一个元素的可枚举序列。也就是说,您可能不希望到处使用 lambda 或 LINQ 表达式。坏消息是我认为没有其他办法。
我怀疑您在这里真正想要的是使用常规方法调用语法隐式应用“转换”操作,同时如果该方法还返回可为空的内容,并且可能提供默认值,则自动应用“折叠”操作尝试直接使用缺失值时的值。这是一个合理的设计,并且使用起来非常方便,但我认为在这里不可能(我自己使用 C# 尝试了几个变体,并且我认为 VB.NET 的差异不足以提供帮助)。像这样的方案的最终结果是能够对空值调用方法,并返回空值而不是空引用异常。作为一个例子,这里有 Ruby 中类似的东西,用一些元编程技巧实现,我认为 VB 没有.NET可以支持。我依稀记得在 Javascript 和其他一些语言中看到过类似的想法。
简而言之,我认为您的想法基本上是正确的,但遗憾的是您想要完成的事情是不可能的。
Well, the definition of
Maybe
itself is simply a single value that may or may not be present, so that much is fine. Incidentally, if you don't like the name "Maybe" for some reason, I'd suggest considering "Option" instead of "Nullable", sinceNullable
is already used by .NET (for the same purpose, even--but only with value types), whileOption
is what F# calls it and consistency is nice.Anyway, there are essentially two things you can do with such a value--apply a transformation to the value (if there is one), and collapse nested layers (e.g., turning a nullable of a nullable of "foo" into just a nullable of "foo"). You can do everything else given those and the ability to construct new values.
The "transform" operation is most straightforwardly implemented by taking a lambda (compare the
Select
method used by LINQ--it's the same thing), and either applying it to the wrapped value or discarding it if there is no value. The "collapse" operation is pretty simple; just keep the inner value if it exists. LINQ is a very natural approach here--you can think ofMaybe
as representing an enumerable sequence of at most one element.That said, you probably don't want to be using lambdas or LINQ expressions everywhere. The bad news is that I don't think there's any other way.
What I suspect you really want here is to apply the "transform" operation implicitly using regular method call syntax, while automatically applying the "collapse" operation if the method also returns something nullable, and possibly providing a default value when attempting to use a missing value directly. This is a reasonable design, and very handy to use, but I don't think it's possible here (I've tried a couple variations myself using C#, and I don't think VB.NET differs enough to help). The end result of a scheme like this is the ability to call methods on null values, and get a null value back instead of a null reference exception. As an illustration, here's something similar in Ruby, implemented with a bit of metaprogramming trickery that I don't think VB.NET can support. I vaguely recall seeing similar ideas done in Javascript and some other languages.
In short, I think you have basically the right idea but what you're trying to accomplish sadly isn't possible.