4

I am trying to create two types where one is able to remove itself from the other such as in this example.

type employee (workplace : Job) =
    member this.Fire () = workplace.Employees.Remove(this) |> ignore
and Job () =
    let employees = new ResizeArray<employee>()
    member this.Employees = employees

But this gets me the compile error of "Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved."

I'm not sure what I am doing wrong here. Any help would be appreciated

Kazurik
  • 183
  • 1
  • 1
  • 6

2 Answers2

4

You can solve the problem even without reordering the declarations - when the F# compiler type-checks the Employee declaration, it doesn't yet know what is the type of workplace.Employees (because the type hasn't been declared yet), so it doesn't know where does the Remove method come from. You can correct that by adding type annotation that specifies that Employees is ResizeArray<Employee>:

type Employee (workplace : Job) =
    member this.Fire () = 
      let emps : ResizeArray<Employee> = workplace.Employees
      emps.Remove(this) |> ignore

and Job () =
    let employees = new ResizeArray<Employee>()
    member this.Employees = employees

However, this example isn't very functional - if you're going to use mutable state (such as ResizeArray), then the state should be hidden as private state of the type (so Jobs could have a Remove method).

In general, declaring recursive type declarations is a bit less comfortable in F# - however, you shouldn't need them that often. Quite frequently, you can use more generic types (i.e. Job may not need to know abou the Employee type).

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • What do you mean by "Quite frequently, you can use more generic types (i.e. Job may not need to know abou the Employee type)." What would I use in its place? In my own code I did end up making my own Fire method in the chat type. – Kazurik Jun 26 '11 at 09:59
  • In this case, `Job` is just a generic collectiob, so it doesn't need to know about `Employee`, but of course, this is just a simplified example - in more complex case, you could use some interface and avoid the recursive type declaration too. I think mutual recursion is probavly more often needed in OO design. – Tomas Petricek Jun 26 '11 at 11:22
2

Try this..

type Job () =
    let employees = new ResizeArray<employee>()
    member this.Employees = employees
and employee (workplace : Job) =
    member this.Fire () = workplace.Employees.Remove(this) |> ignore
Semlar
  • 21
  • 1
  • That does fix it. Do you happen to know why? The only thing I could come up with is that because the compiler goes from the top down it simply has no idea what a Job is. However, I thought the and keyword made the compiler take a second pass at compilation to resolve this. Clearly I was wrong. I don't suppose you know what it does? – Kazurik Jun 26 '11 at 07:11
  • "Job" hasn't been declared yet when you reference it for "employee". – Semlar Jun 26 '11 at 07:18
  • 1
    `type...and` has a two-pass type inference algorithm; first it looks at all the explicit method signatures, top to bottom, and then does a second pass on the method bodies, top to bottom. Here the body of one method needs info from the body of another. – Brian Jun 26 '11 at 17:48
  • (Possibly see also http://stackoverflow.com/questions/844733/why-cant-fs-type-inference-handle-this though that is mostly basics, whereas this is a more advanced aspect of the type inference algorithm for recursive members.) – Brian Jun 26 '11 at 17:49