I figured out the answer to the original question. It was just to use a type variable in the downcast expression. It's up to later type inference to decide just what it means.

1
2
3
let rnew (d:AppDomain) (t:Type) =
  d.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName) :?> 'a
let o = rnew AppDomain.CurrentDomain (type Inspector)

My ultimate objective was to investigate creating objects or executing code in other AppDomains. I realized that trying to create objects in remote AppDomains was very object oriented, and not very functional. So I decided to write an object than enables arbitrary code to be executed in a remote AppDomain. This ended up being another exercise in struggling to learn F# syntax (boy it can be complex).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#light
open System

type Remoter<'a> =
  class
    inherit MarshalByRefObject
    new(f) = { _f = f }
    val _f : unit -> 'a
    member x.Call = x._f()
  end

let rcall (d:AppDomain) (f:unit->'a) =
  let t = (type Remoter<'a>)
  let r = d.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName, true, Reflection.BindingFlags.CreateInstance, null, [| (box f) |], null, null, null)
  (r :?> Remoter<'a>).Call

let inspect (d:AppDomain) =
  let callingDomain = AppDomain.CurrentDomain.FriendlyName 
  rcall d 
    (fun() -> 
      printf "inspector called from: %s\nexecuted in: %s\non thread: %d\n\n\n"
        callingDomain 
        AppDomain.CurrentDomain.FriendlyName
        (System.Threading.Thread.CurrentThread.GetHashCode()))
        
   
let d0 = AppDomain.CurrentDomain
let d1 = AppDomain.CreateDomain("d1")

inspect d0
inspect d1

And finally, I decided I might want to use the built-in method instead, AppDomain.DoCallBack. I suppose if it is to return values, it'll need to do something with mutable state. More to learn!

1
let rcall2 (d:AppDomain) (f:unit->unit) = d.DoCallBack(new CrossAppDomainDelegate(f))
By on 6/6/2007 1:42 AM ()

Hi Sebastian,

Here's an alternative version of your first code:

1
2
3
4
5
6
7
8
 

let createRemote<'T> (d: AppDomain) = 
    unbox<'T>(d.CreateInstanceAndUnwrap((type 'T).Assembly.FullName,(type 'T).FullName))

let o = createRemote<Inspector>(AppDomain.CurrentDomain)

For classes you might like to use implict class construction, which is much, much nicer. This was added in 1.9.1 and is covered in Robert Pickering's book.

1
2
3
4
5
6
7
8
9
10
11
12
13
 

open System
open System.Reflection
open System.Threading

type Remoter<'a>(f) =
  class
    inherit MarshalByRefObject()
    member x.Call : 'a = f()
  end

In 1.9.2 you'll even be able to drop the class/end :-). Here's a slightly teaked version of your second code - no great changes here.

1
2
3
4
5
6
let remoteCall (d:AppDomain) (f:unit->'a) =
  let t = (type Remoter<'a>)
  let r = d.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName, true, BindingFlags.CreateInstance, null, [| box f |], null, null, null)
          |> unbox<Remoter<'a>>
  r.Call

You generally never need to create delegates directly, you just use a syntactic lambda expression instead,

1
let remoteCall2 (d:AppDomain) f = d.DoCallBack(fun () -> f ())

Kind regards

Don

By on 6/7/2007 5:29 AM ()

As always your updates make the code clearer and raise some interesting questions.

In the case of the implicit class construction, I notice that the Call has to be annotated with 'a, otherwise one gets the error "... Type inference has inferred the polymorphic signature member Remoter.Call : '_a with get but 'Call' is constructor or property getter/setter, which cannot be more generic than the enclosing type. Add type constraints to indicate the exact types involved." I have two questions.

1) If we have inferred that Remoter.Call is 'a with get, why doesn't that just automatically make the Remoter class itself a 'a Remoter, just as functions are automatically inferred as polymorphic where possible. [update: upon reflection, this seems like a bad idea for several obvious reasons... so my real question is (2), immediately below]

2) If we don't automatically do (1), then the error reads clearly enough, but I don't quite understand it. Pickering explains in his book this is a CLR restriction. But instance methods can be more generic than their class (e.g. a 'T class can have a 'U Method), why not properties and constructors?

And for the delegate/syntactic lambda, I again wonder what the distinction is. If syntatic lambdas have a "free" conversion to a delegate, why not basic function values? (This is what I was hoping by explicitly constraining f to (unit->unit), whose signature matches the CrossAppDomainDelegate). C# 2.0 and 3.0 have taken this route, enabling function "values" (such as they exist in C#) to have implicit conversions to Delegates. Is this because C#'s type inference problem is more tightly constrained?

Thanks as always for the insightful commentary and quick responses. Honestly, Don, do you ever sleep?! :-)

By on 6/13/2007 1:41 PM ()

Hi Sebastian,

Very good, accurate questions!

Re (1) - automatically inferring that a type (rather than a value) is generic has been considered on F#-like languages, but it never buys much. An inference too far.

Re (2) - properties and constructors can't be more generic than their enclosing class - this is a .NET restriction (and a design decision made during .NET generics, mainly for simplicity). I regret the bit about constructors not being generic - indeed regret them not being static members take a particular instantiation of "this" as their first argument.

As it happens, in this case the thing can't be generic anyway since it's type will involve the type of "f".

Re "syntactic lambdas" - yes, it stems from the fact that the type inference prolem is less constrained. This happens a bit: a few extra annotations here and there, but overall nearly always much shorter code.

Kind regards

Don

By on 6/13/2007 5:00 PM ()

Hi,

Perhaps this would work :

1
2
3
4
let newRemote (d:AppDomain) (ty : System.Type) =
  d.CreateInstanceAndUnwrap(ty.Assembly.FullName, ty.FullName) :?> ty

let i1 = newRemote d1 (type Inspector)
By on 6/6/2007 12:52 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper