Compiler can't tell which record type with dup

2019-08-02 19:41发布

My program has a few record types with the same field names (each record type means something different). The compiler insists function parameters matching this record shape must be of the last type declared, even though I'm declaring record instances with unambiguous field names, and always passing a consistent type into each function.

What is the appropriate way to deal with this? I know I could put a type annotation on the functions, but I feel like if I'm doing things the Right Way, I shouldn't need to fight the compiler with type annotations.

Minimum code demonstrating the problem:

type type1 = {x:int}
type type2 = {x:int}

let getX t =
    t.x

getX {type1.x=1}
|> ignore

Compiler output:

$ fsharpc --nologo test.fs


/tmp/typetest/test.fs(7,6): error FS0001: This expression was expected to have type
    type2    
but here has type
    type1    

标签: f# record
3条回答
▲ chillily
2楼-- · 2019-08-02 19:42

You could use a constraint on getX:

let inline getX (t: ^T) =
    (^T : (member x: int) (t))

However trying to make it more generic (instead of requiring the property x to be int):

let inline getX_generic (t: ^T) : 'U = 
    (^T : (member x : 'U) (t))

has issues with the F# interpreter, compiler and runtime.

The F# interpreter bombs out, the compiler thinks that getX_generic returns an obj instead of int, and when you do run it, it does not compute.

F# being a new language has a few bugs - if you want this to work - I would suggest you log these issues in a bug report.

查看更多
Emotional °昔
3楼-- · 2019-08-02 19:54

There are a few ways around this:

  1. Type annotations as you suggested:

    let getX (t : type1) =
        t.x
    
  2. Define getX before the conflicting type is defined:

    type type1 = {x:int}
    
    let getX t =
        t.x
    
    type type2 = {x:int}
    
  3. Don't explicitly specify the type when calling the function:

    getX {x=1}
    

Which of these options is the 'right' way depends a lot on the precise situation.

查看更多
啃猪蹄的小仙女
4楼-- · 2019-08-02 19:54

In addition to the answer by p.s.w.g, you might consider using modules to partition the scope.

module A =
    type type1 = {x:int}

module B =
    type type2 = {x:int}

module C =
    // only make type1 visible in C
    open A  

    let getX t =
        t.x

    getX {type1.x=1}
    |> ignore

I would say that, in F#, having two records with the same labels in the same namespace is a bit of a code smell.

查看更多
登录 后发表回答