Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't F# compiler infer type in this case?

It seems that the p argument in printPerson function can't be inferred to be Person, but intelisense shows for both of printPerson calls that I'm passing p : Person. Help me understand what I'm doing wrong please?

type Person (name:string) = 
        member e.Name = name

type EmployeeNode = 
    | Leader of Person * int * list<EmployeeNode>
    | Employee of Person * int

let printPerson level p = 
    printfn "%s %s" <| String.replicate (level) "#" <| p.Name 

let rec print node  = 
    match node with
    | Employee(p, level) -> printPerson level p
    | Leader(p, level, nodes) -> 
        printPerson level p
        List.iter print nodes
like image 903
DevNewb Avatar asked Dec 03 '22 23:12

DevNewb


1 Answers

Multiple types could have a member this.Name, even if they don't in this example, so the compiler doesn't know that you meant Person. Say for example you had

type Person (name : string) =
    member this.Name = name

type School (name : string, address : string) =
    member this.Name = name
    member this.Address = address

let printName x = printfn "%s" x.Name       // This is a type error.

The compiler can't tell which you meant - Person or School. It doesn't matter if you haven't defined another type with a member of the same name, the compiler still won't take the type because things like type extensions can add members onto types after compilation.

Intellisense knows the types you're trying to pass when you call the function, but that's not the same as the compiler enforcing type-safety. In general, any function which accesses class methods or members will need a type annotation for that class.

To fix your example, you just need to change to

let printPerson level p =

to

let printPerson level (p : Person) =
like image 182
Jake Lishman Avatar answered Jan 09 '23 05:01

Jake Lishman