Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# - Type parameter cannot be used as type constructor

Tags:

f#

akka.net

I’m working in F# with Akkling so I can use the strongly typed actors on Akka.net but I’ve hit a design limitation within F# and I wondered if there is an elegant way around this.

Take my root message type, I don’t really want to have IActorRef<_> in there because this type will live in a common lib and should not be aware of message system it uses. Also, for easy testing I don’t want to have to create the whole actor system (or test kit).

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

A horrible work around is this:

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

Ideally, I would like this but there is a language limitation (Error: Type parameter cannot be used as type constructor):

type MessageType<'a> =
    | World of WorldMessage<'a>
    | Location of 'a<LocationMessage> * LocationMessage
    | Client of 'a<LocationMessage> * ClientMessage
like image 560
David Teasdale Avatar asked Jun 20 '18 09:06

David Teasdale


1 Answers

The actual type system problem was already mentioned in the comments (lack of HKTs), but I don't think they're really necessary to solve the design problem here.

You don't want a direct dependency on Akka.NET, but you still want your types to carry a notion of having an actor reference to go with the message. One way around is to introduce your own interface around Actors (either as an actual interface type or a set of functions, depending what makes sense in your context).

So in your common library you have your own IMyActorRef with whatever you consider a reasonable common subset of IActorRef functionality:

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

and define your message type (as well as the actual logic that consumes it) in terms of that interface:

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

And then provide the implementation for it at the point where you reference Akka.NET.

like image 137
scrwtp Avatar answered Oct 22 '22 02:10

scrwtp