在 F# 记录上实现具有泛型参数的接口
Implementing an interface with a generic parameter on a F# record
我正在尝试实施 Microsoft.Extensions.Logging.ILogger (copied below for brevity) on a F# Record
using System;
namespace Microsoft.Extensions.Logging
{
public interface ILogger
{
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
bool IsEnabled(LogLevel logLevel);
IDisposable BeginScope<TState>(TState state);
}
}
这是记录实现。
type ILoggerRcd<'TState> =
{
BeginScope : 'TState -> IDisposable
IsEnabled : LogLevel -> bool
Log : LogLevel * EventId * 'TState * exn * Func<'TState,exn,string> -> unit
}
interface ILogger with
override this.BeginScope(state : 'TState): IDisposable =
this.BeginScope (state)
override this.IsEnabled(logLevel: LogLevel): bool =
this.IsEnabled logLevel
override this.Log(logLevel: LogLevel, eventId: EventId, state : 'TState, ``exception``: exn, formatter: Func<'TState,exn,string>): unit =
this.Log(logLevel, eventId, state, ``exception``, formatter)
但是,我在 BeginScope 上收到此错误:One or more of the explicit class or function type variables for this binding could not be generalized, because they were constrained to other types
。
日志中出现此错误:The generic member 'Log' has been used at a non-uniform instantiation prior to this program point. Consider reordering the members so this member occurs first. Alternatively, specify the full type of the member explicitly, including argument types, return type and any additional generic parameters and constraints.
我已经通读了一些关于 fsharp 编译器本身的问题,但似乎没有什么是我遇到的这种情况。这是可以做到的吗?
ILogger
界面要求您可以记录 任何 类型的对象,但您试图仅记录 'TState
类型的对象。
接受BeginScope
的签名:
IDisposable BeginScope<TState>(TState state);
看到那个 <TState>
位了吗?那是一个通用参数。此签名意味着每次任何人调用此方法时,他们 都可以选择一种类型 TState
用于该调用。
同样:调用者 选择泛型类型,而不是实现者。
例如:
let l : ILogger = ...
l.BeginScope 42 // TState = int
l.BeginScope true // TState = bool
这意味着您的 BeginScope
实现必须能够使用 任何 类型,而不仅仅是您的 ILoggerRcd
记录的类型已创建。
这里的问题是您试图限制 ILogger
的 'TState
以匹配 ILoggerRcd
的 'TState
,但这不是您的选择那。
要看到这一点,请注意 ILogger.BeginScope
的调用者可以在一次调用中传递 int
状态,然后在另一次调用中传递 string
状态 相同的 ILogger
个实例 。您的实现试图阻止这种情况,因此会出现编译器错误。
我能看到的解决这个问题的唯一方法是在您的类型中使用泛型方法而不是函数记录。我不认为有任何方法可以使用原始 F# 记录来做你想做的事。
我正在尝试实施 Microsoft.Extensions.Logging.ILogger (copied below for brevity) on a F# Record
using System;
namespace Microsoft.Extensions.Logging
{
public interface ILogger
{
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
bool IsEnabled(LogLevel logLevel);
IDisposable BeginScope<TState>(TState state);
}
}
这是记录实现。
type ILoggerRcd<'TState> =
{
BeginScope : 'TState -> IDisposable
IsEnabled : LogLevel -> bool
Log : LogLevel * EventId * 'TState * exn * Func<'TState,exn,string> -> unit
}
interface ILogger with
override this.BeginScope(state : 'TState): IDisposable =
this.BeginScope (state)
override this.IsEnabled(logLevel: LogLevel): bool =
this.IsEnabled logLevel
override this.Log(logLevel: LogLevel, eventId: EventId, state : 'TState, ``exception``: exn, formatter: Func<'TState,exn,string>): unit =
this.Log(logLevel, eventId, state, ``exception``, formatter)
但是,我在 BeginScope 上收到此错误:One or more of the explicit class or function type variables for this binding could not be generalized, because they were constrained to other types
。
日志中出现此错误:The generic member 'Log' has been used at a non-uniform instantiation prior to this program point. Consider reordering the members so this member occurs first. Alternatively, specify the full type of the member explicitly, including argument types, return type and any additional generic parameters and constraints.
我已经通读了一些关于 fsharp 编译器本身的问题,但似乎没有什么是我遇到的这种情况。这是可以做到的吗?
ILogger
界面要求您可以记录 任何 类型的对象,但您试图仅记录 'TState
类型的对象。
接受BeginScope
的签名:
IDisposable BeginScope<TState>(TState state);
看到那个 <TState>
位了吗?那是一个通用参数。此签名意味着每次任何人调用此方法时,他们 都可以选择一种类型 TState
用于该调用。
同样:调用者 选择泛型类型,而不是实现者。
例如:
let l : ILogger = ...
l.BeginScope 42 // TState = int
l.BeginScope true // TState = bool
这意味着您的 BeginScope
实现必须能够使用 任何 类型,而不仅仅是您的 ILoggerRcd
记录的类型已创建。
这里的问题是您试图限制 ILogger
的 'TState
以匹配 ILoggerRcd
的 'TState
,但这不是您的选择那。
要看到这一点,请注意 ILogger.BeginScope
的调用者可以在一次调用中传递 int
状态,然后在另一次调用中传递 string
状态 相同的 ILogger
个实例 。您的实现试图阻止这种情况,因此会出现编译器错误。
我能看到的解决这个问题的唯一方法是在您的类型中使用泛型方法而不是函数记录。我不认为有任何方法可以使用原始 F# 记录来做你想做的事。