In C #, adding SelectMany extends linq to a new monad type, how do I do the same in VB.net?

Old Another Geek blog post explaining monads describes adding the SelectMany extension method in C # to extend linq syntax to new types.

I tried it in C # and it works. I did a direct conversion to VB.net and it doesn't work. Does anyone know if VB.net supports this feature or how to use it?

Here is C # code that works:

class Identity<T> {
    public readonly T Value;
    public Identity(T value) { this.Value = value; }
}
static class MonadExtension {
    public static Identity<T> ToIdentity<T>(this T value) {
        return new Identity<T>(value);
    }
    public static Identity<V> SelectMany<T, U, V>(this Identity<T> id, Func<T, Identity<U>> k, Func<T, U, V> s) {
        return s(id.Value, k(id.Value).Value).ToIdentity();
    }
}
class Program {
    static void Main(string[] args) {
        var r = from x in 5.ToIdentity()
                from y in 6.ToIdentity()
                select x + y;
    }
}

      

Here is the VB.net code that doesn't work (note: written in vs2010, so some line continuations may be missing):

Imports System.Runtime.CompilerServices

Public Class Identity(Of T)
    Public ReadOnly value As T
    Public Sub New(ByVal value As T)
        Me.value = value
    End Sub
End Class
Module MonadExtensions
    <Extension()> _
    Public Function ToIdentity(Of T)(ByVal value As T) As Identity(Of T)
        Return New Identity(Of T)(value)
    End Function
    <Extension()> _
    Public Function SelectMany(Of T, U, V)(ByVal id As Identity(Of T), ByVal k As Func(Of T, Identity(Of U)), ByVal s As Func(Of T, U, V)) As Identity(Of V)
        Return s(id.value, k(id.value).value).ToIdentity()
    End Function
End Module
Public Module MonadTest
    Public Sub Main()
        ''Error: Expression of type 'Identity(Of Integer)' is not queryable.
        Dim r = From x In 5.ToIdentity() _
                From y In 6.ToIdentity() _
                Select x + y
    End Sub
End Module

      

+2


source to share


2 answers


Obviously, VB.net requires that, in addition to defining SelectMany, the target type must implement the methods you want (e.g. Select, Where, etc.).

Add this method to Identity and the program compiles and runs:

Public Function [Select](Of R)(ByVal projection As Func(Of T, R)) As Identity(Of R)
    Return projection(value).ToIdentity
End Function

      



You can also implement it as an extension method for existing linq-ify types:

<Extension()> _
Public Function [Select](Of T, R)(ByVal this As Identity(Of T), ByVal projection As Func(Of T, R)) As Identity(Of R)
    Return projection(this.value).ToIdentity
End Function

      

Also, VB.net only needs SelectMany if there are multiple "from" lines. If the expression is of the form "from x select x + 1", then only the Identity.Select method should be implemented.

+3


source


Equivalent code must also be supported by VB. Make sure you translate extension methods correctly:

This C #:

public static class MonadExtensions
{
    public static Identity<T> ToIdentity<T>(this T value)
    {
        return new Identity<T>(value);
    }
}

      

Would this VB be:

Imports System.Runtime.CompilerServices

Module MonadExtensions

  <Extension()> _
  Public Function ToIdentity(Of T)(ByVal value As T) As Identity(Of T)
    Return New Identity(Of T)(value)
  End Function

End Module

      

Update: Your code is listed correctly, so it seems like you are just using a VB compiler limitation. As far as I can tell, what you are trying to do is legal according to the language specification.



However, I managed to trick the compiler into accepting the request by Identity(Of T)

pretending that it implements IEnumerable(Of T)

:

Public Class Identity(Of T)
  Implements IEnumerable(Of T)
  Public ReadOnly value As T
  Public Sub New(ByVal value As T)
    Me.value = value
  End Sub

  Public Function GetEnumerator() As IEnumerator(Of T) _
    Implements IEnumerable(Of T).GetEnumerator

    Throw New InvalidOperationException("This should never be called.")
  End Function
  Public Function GetEnumerator1() As IEnumerator _
    Implements IEnumerable(Of T).GetEnumerator

    Throw New InvalidOperationException("This should never be called.")
  End Function
End Class

      

Once we have convinced the compiler that it is a valid request, it correctly resolves the call to your custom SelectMany

.

Update 2: Or, what you said,

Strilanc. I tried this first but apparently forgot the attribute Extension

. From the language specification, something is considered requested if in order of preference ...
  • It defines the corresponding Select method.
  • It has an AsEnumerable () or AsQueryable () method.
  • It has a Cast (Of T) method.
+2


source







All Articles