9

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
  • 12
    More than a language limitation, it's a .NET type system limitation. We don't have higher kinds in .NET which means you can't define a generic of a generic (or a generic of a concrete) in any .NET language. – Gus Jun 20 '18 at 11:41
  • 3
    Sadly, you cannot define a generic of a generic in the .NET type system, so no `'t<'a>` is allowed in F#. You can introduce a type like `Kind<'t, 'a>` that sort of wraps a `'t<'a>`, but it's not as elegant. There's a blog post about doing such an implementation [here](https://robkuz.github.io/HKTS-in-fsharp-part-III-Concept-Emulation/). – Aaron M. Eshbach Jun 20 '18 at 14:25

1 Answers1

4

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.

scrwtp
  • 13,437
  • 2
  • 26
  • 30
  • It's a good idea, I'm just wondering if the akka.net system would be able to serializer / deserializer my abstract type and implementation then sent it to remote actors the same way it does with the built in IActorRef<_>? – David Teasdale Jun 21 '18 at 09:50
  • @DavidTeasdale: You'd need to delegate serialization/deserialization to the underlying implementation somehow, but I don't know what Akka does for serialization to give you a clear solution. Depending on the exact mechanism this may or may not need to be part of your interface. – scrwtp Jun 21 '18 at 10:30
  • I think IActorRefs just serialize as the actor path, so if your type serializes the same way, it should be ok. – Aaron M. Eshbach Jun 22 '18 at 19:13