简体   繁体   English

具有 F# SRTP 约束的 OR 模式被解释为 AND

[英]OR pattern with F# SRTP constraints is interpreted as AND

I'm wondering why the |我想知道为什么| , "or" pattern in a SRTP context in my function doThingsWithOrProps isn't working as expected below, aka it should be able accept a type that has either a property PropA or another property PropB , but it is interpreted as an AND & pattern instead like for the function doThingsWithAndProps which doesn't really make sense since the pattern expected for the input of the respective two functions is clearly different. ,我的函数doThingsWithOrProps中 SRTP 上下文中的“或”模式未按预期工作,也就是它应该能够接受具有属性PropA或另一个属性PropB的类型,但它被解释为 AND &模式就像函数doThingsWithAndProps一样,它实际上没有意义,因为相应两个函数的输入预期模式明显不同。

let inline (|PropA|) source =
    (^Source: (member PropA: 'PropA) source)

let inline (|PropB|) source =
    (^Source: (member PropB: 'PropB) source)

let inline (|PropAAndB|) (PropA (propA: 'PropA) & PropB (propB: 'PropB)) = (propA, propB)

let inline (|PropAOrB|) (PropA p | PropB p) = p

let inline doThingsWithAndProps (PropAAndB (propA: 'PropA, propB: 'PropB)) =
    printfn $"({nameof propA} = %A{propA}, {nameof propB} = %A{propB})"

let inline doThingsWithOrProps (PropAOrB propAOrB) =
    printfn $"{nameof propAOrB} = %A{propAOrB}"

// Compiles just fine
doThingsWithAndProps {| PropA = "hello"; PropB = "world" |}

// The type '{| PropA: 'a |}' does not support the operator 'get_PropB'
doThingsWithOrProps {| PropA = "wer" |}

// The type '{| PropB: 'a |}' does not support the operator 'get_PropA'
doThingsWithOrProps {| PropB = "wer" |}

=== ===

Additional bits using https://sharplab.io to convert valid parts of the F# code above to C# (skimming some part parts about equality, hashcode, to making a little terser)使用https://sharplab.io将上面 F# 代码的有效部分转换为 C# 的其他位(略读一些关于相等性、哈希码的部分,以使其更简洁)

let inline (|PropA|) source =
    (^Source: (member PropA: 'PropA) source)

let inline (|PropB|) source =
    (^Source: (member PropB: 'PropB) source)

let inline (|PropAAndB|) (PropA (propA: 'PropA) & PropB (propB: 'PropB)) = (propA, propB)

let inline (|PropAOrB|) (PropA p | PropB p) = p

let inline doThingsWithAndProps (PropAAndB (propA: 'PropA, propB: 'PropB)) =
    printfn $"({nameof propA} = %A{propA}, {nameof propB} = %A{propB})"

let inline doThingsWithOrProps (PropAOrB propAOrB) =
    printfn $"{nameof propAOrB} = %A{propAOrB}"

// Compiles just fine
doThingsWithAndProps {| PropA = "hello"; PropB = "world" |}
[assembly: FSharpInterfaceDataVersion(2, 0, 0)]
[assembly: AssemblyVersion("0.0.0.0")]

[CompilationMapping(SourceConstructFlags.Module)]
public static class @_
{
    [SpecialName]
    public static Tuple<PropA, PropB> |PropAAndB|$W<a, PropB, PropA>(FSharpFunc<a, PropA> get_PropA, FSharpFunc<a, PropB> get_PropB, a _arg1)
    {
        PropA item = get_PropA.Invoke(_arg1);
        PropB item2 = get_PropB.Invoke(_arg1);
        return new Tuple<PropA, PropB>(item, item2);
    }

    [SpecialName]
    public static b |PropAOrB|<a, b>(a _arg1)
    {
        if (false)
        {
            return (b)(object)null;
        }
        throw new NotSupportedException("Dynamic invocation of get_PropA is not supported");
    }

    [SpecialName]
    public static b |PropAOrB|$W<a, b>(FSharpFunc<a, b> get_PropA, FSharpFunc<a, b> get_PropB, a _arg1)
    {
        return get_PropA.Invoke(_arg1);
    }

    public static void doThingsWithAndProps<a, PropB, PropA>(a _arg1)
    {
        if (0 == 0)
        {
            throw new NotSupportedException("Dynamic invocation of get_PropA is not supported");
        }
        PropA val = (PropA)(object)null;
        if (0 == 0)
        {
            throw new NotSupportedException("Dynamic invocation of get_PropB is not supported");
        }
        PropB val2 = (PropB)(object)null;
        object[] array = new object[4];
        array[0] = "propA";
        array[1] = val;
        array[2] = "propB";
        array[3] = val2;
        Type[] array2 = new Type[2];
        array2[0] = typeof(PropA);
        array2[1] = typeof(PropB);
        ExtraTopLevelOperators.PrintFormatLine(new PrintfFormat<Unit, TextWriter, Unit, Unit, Tuple<string, PropA, string, PropB>>("(%P() = %A%P(), %P() = %A%P())", array, array2));
    }

    public static void doThingsWithAndProps$W<a, PropB, PropA>(FSharpFunc<a, PropA> get_PropA, FSharpFunc<a, PropB> get_PropB, a _arg1)
    {
        PropA val = get_PropA.Invoke(_arg1);
        PropB val2 = get_PropB.Invoke(_arg1);
        object[] array = new object[4];
        array[0] = "propA";
        array[1] = val;
        array[2] = "propB";
        array[3] = val2;
        Type[] array2 = new Type[2];
        array2[0] = typeof(PropA);
        array2[1] = typeof(PropB);
        ExtraTopLevelOperators.PrintFormatLine(new PrintfFormat<Unit, TextWriter, Unit, Unit, Tuple<string, PropA, string, PropB>>("(%P() = %A%P(), %P() = %A%P())", array, array2));
    }

    public static void doThingsWithOrProps<a, b>(a _arg1)
    {
        if (0 == 0)
        {
            throw new NotSupportedException("Dynamic invocation of get_PropA is not supported");
        }
        b val = (b)(object)null;
        object[] array = new object[2];
        array[0] = "propAOrB";
        array[1] = val;
        Type[] array2 = new Type[1];
        array2[0] = typeof(b);
        ExtraTopLevelOperators.PrintFormatLine(new PrintfFormat<Unit, TextWriter, Unit, Unit, Tuple<string, b>>("%P() = %A%P()", array, array2));
    }

    public static void doThingsWithOrProps$W<a, b>(FSharpFunc<a, b> get_PropA, FSharpFunc<a, b> get_PropB, a _arg1)
    {
        b val = get_PropA.Invoke(_arg1);
        object[] array = new object[2];
        array[0] = "propAOrB";
        array[1] = val;
        Type[] array2 = new Type[1];
        array2[0] = typeof(b);
        ExtraTopLevelOperators.PrintFormatLine(new PrintfFormat<Unit, TextWriter, Unit, Unit, Tuple<string, b>>("%P() = %A%P()", array, array2));
    }
}

namespace <StartupCode$_>
{
    internal static class $_
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal static readonly <>f__AnonymousType1562431155<string, string> _arg1@11;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal static readonly string activePatternResult1923786_0@18;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal static readonly string activePatternResult1923786_1@18;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal static readonly PrintfFormat<Unit, TextWriter, Unit, Unit> format@1;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [CompilerGenerated]
        [DebuggerNonUserCode]
        internal static int init@;

        static $_()
        {
            _arg1@11 = new <>f__AnonymousType1562431155<string, string>("hello", "world");
            activePatternResult1923786_0@18 = @_._arg1@11.PropA;
            activePatternResult1923786_1@18 = @_._arg1@11.PropB;
            object[] array = new object[4];
            array[0] = "propA";
            array[1] = @_.activePatternResult1923786_0@18;
            array[2] = "propB";
            array[3] = @_.activePatternResult1923786_1@18;
            Type[] array2 = new Type[2];
            array2[0] = typeof(string);
            array2[1] = typeof(string);
            format@1 = new PrintfFormat<Unit, TextWriter, Unit, Unit, Tuple<string, string, string, string>>("(%P() = %A%P(), %P() = %A%P())", array, array2);
            PrintfModule.PrintFormatLineToTextWriter(Console.Out, @_.format@1);
        }
    }
}

You're confusing runtime with compile time.您将运行时与编译时混淆了。

The SRTP constraints work at compile time. SRTP 约束在编译时起作用。 The compiler has to make sure that any values that could possibly be passed to your function when runtime comes around will definitely satisfy the SRTP constraint.编译器必须确保在运行时出现时可能传递给您的函数的任何值肯定会满足 SRTP 约束。

Active matchers, on the other hand, work at runtime.另一方面,主动匹配器在运行时工作。 It's not known ahead of time what the value passed to the active matcher will be.事先不知道传递给活动匹配器的值是什么。 The active matcher gets the value, looks at it, and determines how it should be classified.主动匹配器获取值、查看它并确定应如何对其进行分类。 That's the whole point of an active matcher: it classifies a value by some classification that's not known in advance.这就是主动匹配器的全部要点:它通过一些事先不知道的分类对值进行分类。

So when you make an active matcher like PropAOrB , the compiler sees that, when a value is passed to it at runtime, your matcher will have to first call PropA to see if it matches the value, and if it doesn't - call PropB and see if it matches.因此,当您制作像PropAOrB这样的活动匹配器时,编译器会发现,当在运行时将值传递给它时,您的匹配器必须首先调用PropA以查看它是否与该值匹配,如果不匹配 - 调用PropB看看它是否匹配。 Which means that, potentially, both PropA and PropB will be called, and therefore, the static (ie known in advance) type of the values that can be passed to PropAOrB must satisfy both PropA 's and PropB 's STRP constraints.这意味着,可能会调用PropAPropB ,因此,可以传递给PropAOrB的值的静态(即提前已知)类型必须同时满足PropAPropB的 STRP 约束。


If you wanted to let PropAOrB accept either values that have only PropA or values that have only PropB or values that have both, one option would be to match at runtime using reflection:如果您想让PropAOrB接受仅具有PropA的值或仅具有PropB的值或同时具有两者的值,一种选择是在运行时使用反射进行匹配:

let inline (|PropA|_|) source =
    let prop = source.GetType().GetProperty("PropA")
    if prop = null then None else Some (PropA (prop.GetValue(source)))

let inline (|PropB|_|) source =
    let prop = source.GetType().GetProperty("PropB")
    if prop = null then None else Some (PropB (prop.GetValue(source)))

let inline (|PropAOrB|) (PropA p | PropB p) = p

let inline doThingsWithOrProps (PropAOrB propAOrB) =
    printfn $"{nameof propAOrB} = %A{propAOrB}"

doThingsWithOrProps {| PropA = "wer" |}

doThingsWithOrProps {| PropB = "wes" |}

But of course, now both PropA and PropB return object (because that's what PropertyInfo.GetValue returns), so you'd have to know the type and cast it I guess.但是当然,现在PropAPropB都返回object (因为那是PropertyInfo.GetValue返回的对象),所以我猜你必须知道类型并转换它。

Plus, you'd have to deal with incomplete pattern matches, because there is a third unhandled possibility: the value has neither PropA nor PropB .另外,您必须处理不完整的模式匹配,因为还有第三种未处理的可能性:该值既没有PropA也没有PropB

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM