F# - 类型参数不能用作类型构造函数

F# - Type parameter cannot be used as type constructor

我在 F# 中与 Akkling 一起工作,因此我可以在 Akka.net 上使用强类型的 actor,但我在 F# 中遇到了设计限制,我想知道是否有一种优雅的方法来解决这个问题。

以我的根消息类型为例,我真的不想在那里有 IActorRef<_>,因为这种类型将存在于一个公共库中,不应该知道它使用的消息系统。此外,为了便于测试,我不想创建整个演员系统(或测试套件)。

type MessageType =
    | World of WorldMessage
    | Location of IActorRef<LocationMessage> * LocationMessage
    | Client of IActorRef<LocationMessage> * ClientMessage

一个糟糕的解决方法是:

type MessageType<'LocationActor, 'PlayerActor, 'ClientActor> =
    | World of WorldMessage<'ClientActor>
    | Location of 'LocationActor * LocationMessage<'ClientActor>
    | Client of 'ClientActor * ClientMessage<'LocationActor>

理想情况下,我想要这个但是存在语言限制(错误:类型参数不能用作类型构造函数):

type MessageType<'a> =
    | World of WorldMessage<'a>
    | Location of 'a<LocationMessage> * LocationMessage
    | Client of 'a<LocationMessage> * ClientMessage

评论中已经提到了实际的类型系统问题(缺少 HKT),但我认为它们对于解决这里的设计问题并不是真正必要的。

您不希望直接依赖于 Akka.NET,但您仍然希望您的类型带有一个概念,即让 actor 引用与消息一起使用。一种解决方法是围绕 Actors 引入您自己的界面(作为实际界面类型或一组函数,具体取决于您的上下文中的意义)。

因此,在您的公共图书馆中,您拥有自己的 IMyActorRef 以及您认为合理的 IActorRef 功能的公共子集:

type IMyActorRef<'msg> = 
   abstract member Tell: ... -> ...
   abstract member Ask: ... -> ...

并根据该接口定义您的消息类型(以及使用它的实际逻辑):

type MessageType =
    | World of WorldMessage
    | Location of IMyActorRef<LocationMessage> * LocationMessage
    | Client of IMyActorRef<ClientMessage> * ClientMessage

然后在您引用 Akka.NET 的地方提供它的实现。