My first disappointment on F# type system.


Today I found that there are examples of code that correct for C# and could not be compiled in F#. I was very surprised and upset.

I continued playing with Neo4jClient and tried to implement more complex model. I have found that it  is not possible to define F# type for cross entity relationship. To make such relationship I need to define type that implements two interfaces, like this:

type FollowRelationship(target) =
    inherit Relationship(target)
    interface IRelationshipAllowingSourceNode<Person>
    interface IRelationshipAllowingTargetNode<Company>

    override this.RelationshipTypeKey
        with get() = "follow"

But F# compiler does not allow the creation of such type. I’ve got the following compilation error:

This type implements or inherits the same interface at different generic instantiations ‘IRelationshipAllowingParticipantNode’ and ‘IRelationshipAllowingParticipantNode’. This is not permitted in this version of F#.

It happens because IRelationshipAllowingSourceNode and IRelationshipAllowingTargetNode inherited from a single generic interface IRelationshipAllowingParticipantNode and F# does not allow to implement the same interface in different generic instantiations.

Here is an implementation of these interfaces from Neo4jClient source code.

public interface IRelationshipAllowingParticipantNode<out TNode>
{
}
public interface IRelationshipAllowingSourceNode<out TNode>
    : IRelationshipAllowingParticipantNode<TNode>
{
}
public interface IRelationshipAllowingTargetNode<out TNode>
    : IRelationshipAllowingParticipantNode<TNode>
{
}

As I found, there is actually no way to do it in F#. An only option is to write such types in C#. We have a similar question about this on StackOverflow: “Implementing the same interface at different generic instantiations“.

May be it is not a real constrain of F#, but it adds a noise to C#/F# integration. It is means that not all C# design patterns are integrable with F#.

It can be one more answer to

About these ads

9 Responses to My first disappointment on F# type system.

  1. Pingback: F# Weekly #13, 2013 | Sergey Tihon's Blog

  2. Joel says:

    I suppose you could make a generic class in C# that inherits Relationship and implements both interfaces, and then inherit that class in F#. That would at least allow you to re-use the base class multiple times instead of writing C# for every relationship you want to create. If it worked, maybe the Neo4jClient people would accept a pull request to put that base class right into the client library.

  3. Joel says:

    Hmm, it seems that even C# can’t compile such a base class. Something like this (hopefully my angle brackets won’t be eaten)…

    public class RelationshipBase :
    Relationship,
    IRelationshipAllowingSourceNode,
    IRelationshipAllowingTargetNode
    {
    }

    …gives this C# compiler error:

    ‘Neo4jClient.RelationshipBase’ cannot implement both ‘Neo4jClient.IRelationshipAllowingParticipantNode’ and ‘Neo4jClient.IRelationshipAllowingParticipantNode’ because they may unify for some type parameter substitutions.

    If you google that message, “because they may unify for some type parameter substitutions,” you’ll find a whole lot of discussion about how this pattern followed by Neo4jClient is generally frowned upon even in C# land.

  4. Joel says:

    …and it ate my angle brackets. Substituting square ones:

    public class RelationshipBase[TSource, TTarget] :
    Relationship,
    IRelationshipAllowingSourceNode[TSource],
    IRelationshipAllowingTargetNode[TTarget]
    {
    }

    • Sergey Tihon says:

      Thank you for your attempt to hack it. I do not find a solution other than implementing data model in C# or try to change Neo4jClient API.

  5. Can you use the “and” keyword with your definition of type FollowRelationship(target) to get around the same interface in multiple generic instantiations problem?

    Notice both these implementations of Vector implement the same interfaces twice by using “and”:

    https://github.com/fsharp/fsharpx/blob/master/src/FSharpx.Collections.Experimental/Vector.fs

    https://github.com/fsharp/fsharpx/blob/master/src/FSharpx.Core/Collections/Vector.fs

    • Sergey Tihon says:

      Sorry, I did not catch the idea. Using `and` keyword I will define two types, but I need the one that implement two different generic instances of the same interface.

  6. I talked to Don about this issue a few days ago in N.Y. It is very significant to the type system, as evidenced by a specific message from the compiler. It has to do the way F# does type unification, http://en.wikipedia.org/wiki/Unification_(computer_science), so this will just never go away. It is an incompatibility between C# and F#.

    • Sergey Tihon says:

      It is bad news. But, thank you, that you do not give up and brought it to the end.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 144 other followers

%d bloggers like this: