首页 > 解决方案 > 编译器无法从共享的“头”文件中解析类型

问题描述

在 Common.fs 中:

namespace Bug
module Common =
    type SourceEntity = {
        id : int
        link : string
    }
    type ReleaseEntity = {
        id : int
        notes : string
    }

在 Release.fs

namespace Bug
open System
open Common
module Release =
    let cache = new Collections.Generic.Dictionary<int, ReleaseEntity>()
    let AddToCache(entity) =
        cache.Add(entity.id, entity)
        ()
    let AddRec() =
        let entity : ReleaseEntity = {
            id = 1
            notes = "Notes"
        }
        AddToCache(entity)

在 Source.fs 中

namespace Bug
open System
open Common
module Source =
    let Cache = new Collections.Generic.Dictionary<int, SourceEntity>()
    let AddToCache(entity) =
        Cache.Add(entity.id, entity) <<=== E R R O R
        ()
    let AddRec() =
        let ent : SourceEntity = {
            id = 1
            releases = "Releases"
        }
        AddToCache(ent)  <<=== E R R O R

Visual Studio 项目中按上述顺序包含的文件。

Source.fs 中报告的错误:错误 FS0001 此表达式应具有“SourceEntity”类型,
但此处具有“ReleaseEntity”类型

如果Common.fs中两种类型的顺序颠倒,Release.fs中报错,其中预期类型为ReleaseEntity,但类型为SourceEntity。

任何想法为什么会发生此错误?

标签: f#

解决方案


当您遇到类型错误时,请尝试考虑编译器是如何推断该特定类型的。

这里:

let AddToCache(entity) =
    cache.Add(entity.id, entity)
    ()

编译器能知道哪个类型有一个id字段entity吗?如果您输入了

let entity = { id = 1; link  = "" }

编译器会推断这是SourceEntity因为只有SourceEntity那些特定的记录字段。在cache.Add(entity.id, entity)中,编译器没有其他约束,除了它必须有一个id字段,所以它选择最后一个匹配类型 - 这就是你得到错误的原因。

如果将公共 id 字段重构为

namespace Bug
module Common =
    type SourceEntity = {
        source_id : int
        link : string
    }
    type ReleaseEntity = {
        release_id : int
        notes : string
    }

你会发现错误消失了。

解决方案

所有解决方案都涉及将其限制为已知类型。

最简单的就是添加类型注解:

let AddToCache(entity: SourceEntity) =

另一个是明确地解构它:

let { SourceEntity.id = id } = entity
Cache.Add(id, entity) 

另一个是强制类型 - 这在这里不相关,但它可能会在未来变得有用:

Cache.Add((entity :> SourceEntity).id, entity) 

我推荐这篇来自 F# 的文章,以获得关于类型推断的乐趣和收益,以便很好地解释该过程。

附言

您实际上只需要一种类型的注释。其余的可以推断:)

module Source =
    let Cache = new Collections.Generic.Dictionary<_, _>()
    let AddToCache (entity: SourceEntity) =
        Cache.Add(entity.id, entity) 
        ()
    let AddRec () =
        let ent = {
            id = 1
            link = ""
        }
        AddToCache(ent)  

推荐阅读