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

Dav*_*ale 10 f# akka.net

我正在使用Akkling在F#工作,所以我可以使用Akka.net上的强类型演员,但是我在F#中遇到了设计限制,我想知道是否有一种优雅的方式.

拿我的根消息类型,我真的不想在那里有IActorRef <_>,因为这种类型将存在于一个公共库中,并且不应该知道它使用的消息系统.此外,为了便于测试,我不想创建整个actor系统(或测试工具包).

type MessageType =
    | World of WorldMessage
    | Location of IActorRef<LocationMessage> * LocationMessage
    | Client of IActorRef<LocationMessage> * ClientMessage
Run Code Online (Sandbox Code Playgroud)

一个可怕的工作是:

type MessageType<'LocationActor, 'PlayerActor, 'ClientActor> =
    | World of WorldMessage<'ClientActor>
    | Location of 'LocationActor * LocationMessage<'ClientActor>
    | Client of 'ClientActor * ClientMessage<'LocationActor>
Run Code Online (Sandbox Code Playgroud)

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

type MessageType<'a> =
    | World of WorldMessage<'a>
    | Location of 'a<LocationMessage> * LocationMessage
    | Client of 'a<LocationMessage> * ClientMessage
Run Code Online (Sandbox Code Playgroud)

scr*_*wtp 5

实际的类型系统问题已经在评论中提到(缺少HKT),但我不认为他们真的有必要在这里解决设计问题.

您不希望直接依赖于Akka.NET,但是您仍然希望您的类型带有一个带有actor引用的概念.一种方法是在Actors周围引入您自己的界面(作为实际的接口类型或一组函数,取决于您的上下文中有意义的).

因此,在您的公共库中,您可以使用自己IMyActorRef认为合理的常用IActorRef功能子集:

type IMyActorRef<'msg> = 
   abstract member Tell: ... -> ...
   abstract member Ask: ... -> ...
Run Code Online (Sandbox Code Playgroud)

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

type MessageType =
    | World of WorldMessage
    | Location of IMyActorRef<LocationMessage> * LocationMessage
    | Client of IMyActorRef<ClientMessage> * ClientMessage
Run Code Online (Sandbox Code Playgroud)

然后在您引用Akka.NET时提供它的实现.