louthy / language-ext

C# functional language extensions - a base class library for functional programming
MIT License
6.48k stars 417 forks source link

How to get Ord<Record<T>> working when members are Set<U> or Arr<U> #293

Closed bradphelan closed 6 years ago

bradphelan commented 7 years ago

I have the following test

    public class ContainerOrderingTests
    {
        public class WithSet : Record<WithSet>
        {
            public WithSet(int i, Set<int> s)
            {
                I = i;
                S = s;
            }

            public int I { get; }
            public Set<int> S { get; }
        }

        [Fact]
        public void SetOrderingWorks()
        {
            var a = new WithSet(0, Prelude.Set(0,1,2));
            var b = new WithSet(0, Prelude.Set(0,1,2));
            Assert.Equal(0,a.CompareTo( b )) ;
        }
    }

which fails with the fact that Set does not implement IComparable. But somehow I'm sure you have thought of this but the solution is non obvious.

bradphelan commented 7 years ago

I tried

    public class ContainerOrderingTests
    {
        public class WithSet : Record<WithSet>
        {
            public WithSet(int i, Set<OrdInt,int> s)
            {
                I = i;
                S = s;
            }

            public int I { get; }
            public Set<OrdInt,int> S { get; }
        }

        [Fact]
        public void SetOrderingWorks()
        {
            var a = new WithSet(0, Prelude.Set<OrdInt,int>(0,1,2));
            var b = new WithSet(0, Prelude.Set<OrdInt,int>(0,1,2));
            Assert.Equal(0,a.CompareTo( b )) ;
        }
    }

but I think that is really just the same as the first example

bradphelan commented 7 years ago

Progress. I now have

    /// <summary>
    /// The pattern we are trying to match here is
    /// <![CDATA[M<N<T>>]]>
    /// which for examples models <![CDATA[Ord<Set<T>>]]>
    /// The generated type needs to match <![CDATA[MN<MT,T>]]> 
    /// or for our concrete example <![CDATA[OrdSet<OrdInt,Int32>]]>
    /// </summary>
    /// <returns></returns>
    public static Option<Type> GetOrderableContainerOrd()
    {

        Option<TypeInfo> ArityOneGeneric(TypeInfo baseInfo)
        {
            return baseInfo.GenericTypeArguments == null || baseInfo.GenericTypeArguments.Length != 1
                ? (Option<TypeInfo>) None
                : baseInfo.GenericTypeArguments[0].GetTypeInfo();
        }

        string RemoveGenerics(string name) => FirstCharToUpper(OrdNameMaps(name.Split( '`' ).Head()));

        var mType = typeof(A).GetTypeInfo();

        return
            from nType in ArityOneGeneric( mType )
            from tType in ArityOneGeneric( nType )
            let mnTypeName = RemoveGenerics( mType.Name ) + RemoveGenerics( nType.Name ) + "`2"
            let mtTypeName = RemoveGenerics( mType.Name ) + RemoveGenerics( tType.Name )
            let tTypeName = RemoveGenerics( tType.Name )
            from mnType in ClassInstancesAssembly.Structs.Where( t => t.Name == mnTypeName ).HeadOrNone()
            from ntType in ClassInstancesAssembly.Structs.Where( t => t.Name == mtTypeName ).HeadOrNone()
            select mnType.MakeGenericType( ntType, tType );

    }

    static string OrdNameMaps(string typename)
    {
        switch (typename)
        {
            case "Int32": return "Int";
            case "Int64": return "Long";
            default: return typename;
        }
    }

    public static string FirstCharToUpper(string input)
    {
        switch (input)
        {
            case null: throw new ArgumentNullException(nameof(input));
            case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
            default: return input.First().ToString().ToUpper() + input.Substring(1);
        }
    }

which can resolve the test

    [Fact]
    public void GetOrderableContainerOrderWorks()
    {
        var type = Class<Ord<Set<int>>>.GetOrderableContainerOrd();
        Assert.True( type.IsSome );

    }
louthy commented 7 years ago

@bradphelan Hi Brad, I've been away for a week or so, so I'll take a look at this once my head is back in gear.

louthy commented 6 years ago

@bradphelan Hi Brad, sorry for the delay. Map<K,V>, Map<OrdK, K, V>, Set<A>, Set<OrdA, A>, Lst<A>, Lst<PredA, A>, Lst<PredList, PredItem, A> now all support IComparable as well as the <, <=, >=, > operators. So your records should also use those.

bradphelan commented 6 years ago

Super :)

On Tue, 14 Nov 2017, 16:32 Paul Louth notifications@github.com wrote:

@bradphelan https://github.com/bradphelan Hi Brad, sorry for the delay. Map<K,V>, Map<OrdK, K, V>, Set, Set<OrdA, A>, Lst, Lst<PredA, A>, Lst<PredList, PredItem, A> now all support IComparable as well as the <, <=, >=, > operators

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/louthy/language-ext/issues/293#issuecomment-344295849, or mute the thread https://github.com/notifications/unsubscribe-auth/AABE8gMYcBt5c1B5KXjJ34w44brdHH8Yks5s2bJygaJpZM4QD4ND .