提问人:Natalie Perret 提问时间:12/22/2022 最后编辑:Natalie Perret 更新时间:12/23/2022 访问量:111
具有 F# SRTP 约束的 OR 模式被解释为 AND
OR pattern with F# SRTP constraints is interpreted as AND
问:
我想知道为什么我的函数中 SRTP 上下文中的“or”模式没有按预期工作,也就是它应该能够接受具有属性或其他属性的类型,但它被解释为 AND 模式,而不是像函数一样,这并没有真正意义,因为各自两个函数的输入所期望的模式明显不同。|
doThingsWithOrProps
PropA
PropB
&
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" |}
===
使用 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 = @[email protected];
activePatternResult1923786_1@18 = @[email protected];
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);
}
}
}
答:
您将运行时与编译时混淆了。
SRTP 约束在编译时起作用。编译器必须确保在运行时可能传递给函数的任何值都绝对满足 SRTP 约束。
另一方面,活动匹配器在运行时工作。事先不知道传递给活动匹配器的值是什么。活动匹配器获取值,查看该值,并确定应如何对其进行分类。这就是主动匹配器的全部意义所在:它通过事先不知道的分类对值进行分类。
因此,当您创建像 这样的活动匹配器时,编译器会发现,当在运行时将值传递给它时,您的匹配器必须首先调用以查看它是否与该值匹配,如果没有 - 调用并查看它是否匹配。这意味着,可能同时调用 和 ,因此,可以传递到的值的静态(即预先已知)类型必须同时满足 's 和 的 STRP 约束。PropAOrB
PropA
PropB
PropA
PropB
PropAOrB
PropA
PropB
如果要允许接受仅具有的值或仅具有的值或同时具有两者的值,则一个选项是在运行时使用反射进行匹配: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" |}
但是,当然,现在两者都是 return (因为这就是 return),所以你必须知道类型并投射它,我猜。PropA
PropB
object
PropertyInfo.GetValue
另外,您必须处理不完整的模式匹配,因为还有第三种未处理的可能性:值既没有也没有。PropA
PropB
评论