Danny G
Danny G

Reputation: 611

Akka.NET can C# actors be created within F# system?

I'm trying to use some ReceiveActors defined in a C# library within an F# system. I tried creating the non-F# API ActorSystem but the call to system.ActorOf(Props.Create(fun () -> new CSharpActor())) doesn't work saying the function argument is not compatible.

I also couldn't find any documentation in the F# API pages on how to create actors defined in a C# library. Is this just not done? Is it generally a "bad" design - i.e. should the actor system be created within the library itself?

EDIT: Code that I am playing with below

C# code

namespace CsActors {
  using Akka.Actor;
  using System;

  public class CsActor : ReceiveActor {
    public CsActor() {
      Receive<string>(msg => { Console.WriteLine($"C# actor received: {msg}"); });
    }
  }

  public class CsActorWithArgs : ReceiveActor {
    public CsActorWithArgs(string prefix) {
      Receive<string>(msg => { Console.WriteLine($"{prefix}: {msg}"); });
    }
  }
}

F# script

#I @"../build"

#r @"Akka.dll"
#r @"Akka.FSharp.dll"
#r @"CsActors.dll"

open Akka.Actor
open Akka.FSharp
open CsActors

let system = System.create "fcmixed" (Configuration.load())

// fails at runtime with "System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.NewExpression'."
//let c1 = system.ActorOf(Props.Create(fun _ -> CsActor()))

// works if CsActor has constructor with no arguments
let c2 = system.ActorOf<CsActor> "c2"
c2 <! "foo"

// if actor doesn't have default constructor - this won't compile
//let c3 = system.ActorOf<CsActorWithArgs> "c3"

// Horusiath solution works for actors requiring arguments
let c4 = system.ActorOf(Props.Create(typeof<CsActorWithArgs>, [| box "c4-prefix" |]))
c4 <! "foo"


// Just for fun trying to use suggestion by dumetrulo (couldn't quite get it to work...)
// copied Lambda module from http://www.fssnip.net/ts/title/F-lambda-to-C-LINQ-Expression
//module Lambda =
//    open Microsoft.FSharp.Linq.RuntimeHelpers
//    open System.Linq.Expressions
//    let toExpression (``f# lambda`` : Quotations.Expr<'a>) =
//        ``f# lambda``
//        |> LeafExpressionConverter.QuotationToExpression
//        |> unbox<Expression<'a>>
//let c5 = system.ActorOf(Props.Create(<@ (fun _ -> CsActorWithArgs "c5-prefix") @> |> Lambda.toExpression))
//c5 <! "foo"

Upvotes: 4

Views: 550

Answers (1)

Bartosz Sypytkowski
Bartosz Sypytkowski

Reputation: 7542

Props.Create with function won't work, because what Props.Create(() => new Actor(a, b)) in C# does is actually taking an expression, and deconstructing it into actor type and constructor arguments. This is necessary, because one of the Props requirements is that it has to be serializable.

What you can do is to use another overload of Props.Create(typeof<MyActor>, [| box myArg1; box myArg2 |]) which essentially does the same, just without compile type safety.

That being said, it'd be better if you'd just use Akkling or Akka.FSharp API when possible.

Upvotes: 3

Related Questions