F# quotations, arrays and self-identifier in constructors
f#, websharper
Solution
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:
- I really want to use arrays - that should be OK
- I really need a self-identifier - it is never necessary, just as constructors are not
- I really need classes (i.e. functional style won't work) - definitely not true
- Per-method self-identifiers (member this.Foo) work fine - yes, and are useful
Problem
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: - I really want to use arrays, because I want WebSharper to translate my collections to JavaSript arrays. - I really need a self-identifier in constructors. - I really need classes (i.e. functional style won't work). - Per-method self-identifiers (`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?