I think that's a well-known limitation of F# but I couldn't find any good workarounds…
So, here is the code (I tried to make it as simple as possible, so probably it looks like it doesn't make any sense):
[<ReflectedDefinition>]
type Human (makeAName: unit -> string) as self =
let mutable cats : Cat array = [| |]
do
// get a cat
cats <- Array.append cats [| new Cat (self, makeAName ()) |]
member this.Cats = cats
and
[<ReflectedDefinition>]
Cat (owner : Human, name : string) = class end
The compiler says:
error FS0452: Quotations cannot contain inline assembly code or pattern matching on arrays
Actually it is the combination of as self
and array property getter that breaks everything.
The points here are:
member this.Foo
) work fine.One workaround I can think of is making constructors private and using static methods to construct objects. This way I don't need as self
. But it is just silly.
Are there any better options?
Update:
Here is an even simpler example:
[<ReflectedDefinition>]
type User (uid: int) as self =
let ROOT_UID = 0
member this.isRoot = (uid = ROOT_UID)
With as self
I can't even define a class constant. Well, it's actually a separate question, but I'll ask it here: how do I define a class constant in this particular case?
I do not think it is silly at all. We actually prefer static constructor methods for clarity, even in code that does not use WebSharper. In the whole IntelliFactory codebase we rarely, if ever use self
.
You are hitting two annoying limitations of F# compiler and quotations. As you point out, static methods can solve the self
problem:
[<ReflectedDefinition>]
type Human private (cats: ref<Cat []>) =
member this.Cats = !cats
static member Create(makeAName: unit -> string) =
let cats = ref [| |]
let h = Human(cats)
let cat = Cat(h, makeAName())
cats := [| cat |]
h
and [<ReflectedDefinition>] Cat (owner: Human, name: string) =
class
end
There are many other ways to accomplish this, for example you can get rid of ref
indirection.
Second, you often get FS0452
in ReflectedDefinition
code with array operations, even in plain static methods. This usually can be resolved by using library functions instead of direct array access (Array.iter
, Array.map
).
For the second example, you really want this:
[<ReflectedDefinition>]
module Users =
[<Literal>]
let ROOT_UID = 0
type User(uid: int) =
member this.isRoot = (uid = ROOT_UID)
The [<Literal>]
annotation will let you pattern-match on your constants, which can be handy if there is more than one.
For your points: