beefytech / Beef

Beef Programming Language
http://www.beeflang.org
Other
2.5k stars 129 forks source link

[Enhancement] Determine if the compiler can do a proper implicit T #706

Open xposure opened 3 years ago

xposure commented 3 years ago

90d3d3a

I think the compiler should be able to determine the implicit generic type here. The param in the method signature is T.

using Atma.Framework;
namespace Atma.Framework
{
    public extension Core
    {
        public static SpriteFont DefaultFont;

        static this
        {
            //works with specifying the type
            Core.Emitter.AddObserver<CoreEvents.GameInitialize>(new => GameInit);
        }

        static ~this()
        {
            //compiler can't determine type (should be CoreEvents.GameInitialize)
            Core.Emitter.RemoveObserver(scope => GameInit);
        }

        static void GameInit(CoreEvents.GameInitialize e)
        {
            DefaultFont = Core.Assets.Load<SpriteFont>(@"fonts/PressStart2P.ttf");
        }
    }
}
bfiete commented 3 years ago

This doesn't work in C# - if it WERE allowed then it would only work on methods that had no overloads at all. I agree with their design decision to not allow it.

public static void Bind<T>(Action<T> dlg)
{

}

public static void MethodA(int a)
{

}

public static void Main(String[] args)
{
    // This works
    Bind<int>(MethodA);
    // This does not work
    Bind(MethodA);            
}
xposure commented 3 years ago

Any chance you have a link or more information for C# ? I wouldn't mind reading over it.

bfiete commented 3 years ago

I guess it must be in here somewhere https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-334.pdf

xposure commented 3 years ago

Seems like its related to this https://stackoverflow.com/questions/3630153/generic-extension-method-type-argument-cannot-be-inferred-from-the-usage/3630202#3630202

There appears to be debate if this should be the correct behavior.

As for method overloading, from that I could understand, C# just matches the more specialized version. If you have a scenario where they are equally specialized it just throws an ambigious error.

bfiete commented 3 years ago

What I meant was this:

public static void Bind<T>(Action<T> dlg)
{

}

public static void MethodA(int a)
{

}

public static void MethodA(float a)
{

}

public static void Main(String[] args)
{
    // This works
    Bind<int>(MethodA);
    // This does not work
    Bind(MethodA);            
}

There is absolutely no way to infer which MethodA was the desired one.

bfiete commented 3 years ago

You may think that this should be handled as a normal "ambiguous method" just like at an invocation callsite, but in those cases you CAN add a new method overload without causing errors, whereas this requested feature would 100% fail every time an overload was added.

xposure commented 3 years ago

If you would figure out how to handle my discriminated union types, this wouldn't be an issue ;)

xposure commented 3 years ago

This isn't a one to one comparison, here is something that fails to match but works in C#. In the spec it says it will infer a return type if all the other types are known.

    //public delegate Result Func<T, Result>(T t);
    public static class Test
    {
        public struct XYZ
        {
            public int x;
            public int y;
        }

        public static K Min<T, K, F>(this List<T> items, Func<T, K> dlg)
            //where bool : operator K < K
        {
            var enumerator = items.GetEnumerator();

            if (!enumerator.MoveNext())
                return default;

            var t = dlg(enumerator.Current);
            while (enumerator.MoveNext())
            {
                var  r = dlg(enumerator.Current);
                //if (r < t) removed due to lack of operator constraints
                    t = r;
            }

            return t;
        }

        public static void TestMe()
        {
            var test = new List<XYZ>();
            var t = test.Min(it => it.x);
            //          let t = test.Min((it) => it.x)); // Can't determine K
        }
    }