- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我们这里变得毛茸茸的。我已经在数据的具体表示上测试了一堆树同步代码,现在我需要对其进行抽象,以便它可以与支持正确方法的任何源和目标一起运行。 [实际上,这将是 Documentum、SQL 层次结构和文件系统等来源;具有 Solr 等目的地和自定义 SQL 交叉引用存储。]
棘手的部分是,当我向下递归类型 T
的树并同步到类型 U
的树时,在某些文件中,我需要将第二种类型 V
与该类型 U
进行“子同步”在当前节点。 ( V
表示文件内部的层次结构...)当我尝试将子同步添加到 V
时,F# 中的类型推断引擎就让我在这个问题上兜圈子。
我用 TreeComparison<'a,'b>
来表示它,因此上面的内容会产生 TreeComparison<T,U>
和 TreeComparison<V,U>
的子比较。
问题是,一旦我在其中一个类方法中提供具体的 TreeComparison<V,'b>
,当我希望第一个类型参数保持通用( V
)时,when 'a :> ITree
类型就会传播到所有推断。也许我可以对 TreeComparison<V,'b>
值进行一些输入?或者,更有可能的是,这个推论实际上告诉我,我思考这个问题的方式本质上是有问题的。
这确实很难压缩,但我想提供工作代码,您可以将其粘贴到脚本中并进行实验,因此开头有大量类型...如果您愿意,核心内容就在最后跳过。大多数通过 ITree 进行的跨类型的实际比较和递归都被砍掉了,因为没有必要看到我正在努力解决的推理问题。
open System
type TreeState<'a,'b> = //'
| TreeNew of 'a
| TreeDeleted of 'b
| TreeBoth of 'a * 'b
type TreeNodeType = TreeFolder | TreeFile | TreeSection
type ITree =
abstract NodeType: TreeNodeType
abstract Path: string
with get, set
type ITreeProvider<'a when 'a :> ITree> = //'
abstract Children : 'a -> 'a seq
abstract StateForPath : string -> 'a
type ITreeWriterProvider<'a when 'a :> ITree> = //'
inherit ITreeProvider<'a> //'
abstract Create: ITree -> 'a //'
// In the real implementation, this supports:
// abstract AddChild : 'a -> unit
// abstract ModifyChild : 'a -> unit
// abstract DeleteChild : 'a -> unit
// abstract Commit : unit -> unit
/// Comparison varies on two types and takes a provider for the first and a writer provider for the second.
/// Then it synchronizes them. The sync code is added later because some of it is dependent on the concrete types.
type TreeComparison<'a,'b when 'a :> ITree and 'b :> ITree> =
{
State: TreeState<'a,'b> //'
ATree: ITreeProvider<'a> //'
BTree: ITreeWriterProvider<'b> //'
}
static member Create(
atree: ITreeProvider<'a>,
apath: string,
btree: ITreeWriterProvider<'b>,
bpath: string) =
{
State = TreeBoth (atree.StateForPath apath, btree.StateForPath bpath)
ATree = atree
BTree = btree
}
member tree.CreateSubtree<'c when 'c :> ITree>
(atree: ITreeProvider<'c>, apath: string, bpath: string)
: TreeComparison<'c,'b> = //'
TreeComparison.Create(atree, apath, tree.BTree, bpath)
/// Some hyper-simplified state types: imagine each is for a different kind of heirarchal database structure or filesystem
type T( data, path: string ) = class
let mutable path = path
let rand = (new Random()).NextDouble
member x.Data = data
// In the real implementations, these would fetch the child nodes for this state instance
member x.Children() = Seq.empty<T>
interface ITree with
member tree.NodeType =
if rand() > 0.5 then TreeFolder
else TreeFile
member tree.Path
with get() = path
and set v = path <- v
end
type U(data, path: string) = class
inherit T(data, path)
member x.Children() = Seq.empty<U>
end
type V(data, path: string) = class
inherit T(data, path)
member x.Children() = Seq.empty<V>
interface ITree with
member tree.NodeType = TreeSection
end
// Now some classes to spin up and query for those state types [gross simplification makes these look pretty stupid]
type TProvider() = class
interface ITreeProvider<T> with
member this.Children x = x.Children()
member this.StateForPath path =
new T("documentum", path)
end
type UProvider() = class
interface ITreeProvider<U> with
member this.Children x = x.Children()
member this.StateForPath path =
new U("solr", path)
interface ITreeWriterProvider<U> with
member this.Create t =
new U("whee", t.Path)
end
type VProvider(startTree: ITree, data: string) = class
interface ITreeProvider<V> with
member this.Children x = x.Children()
member this.StateForPath path =
new V(data, path)
end
type TreeComparison<'a,'b when 'a :> ITree and 'b :> ITree> with
member x.UpdateState (a:'a option) (b:'b option) =
{ x with State = match a, b with
| None, None -> failwith "No state found in either A and B"
| Some a, None -> TreeNew a
| None, Some b -> TreeDeleted b
| Some a, Some b -> TreeBoth(a,b) }
member x.ACurrent = match x.State with TreeNew a | TreeBoth (a,_) -> Some a | _ -> None
member x.BCurrent = match x.State with TreeDeleted b | TreeBoth (_,b) -> Some b | _ -> None
member x.CreateBFromA =
match x.ACurrent with
| Some a -> x.BTree.Create a
| _ -> failwith "Cannot create B from null A node"
member x.Compare() =
// Actual implementation does a bunch of mumbo-jumbo to compare with a custom IComparable wrapper
//if not (x.ACurrent.Value = x.BCurrent.Value) then
x.SyncStep()
// And then some stuff to move the right way in the tree
member internal tree.UpdateRenditions (source: ITree) (target: ITree) =
let vp = new VProvider(source, source.Path) :> ITreeProvider<V>
let docTree = tree.CreateSubtree(vp, source.Path, target.Path)
docTree.Compare()
member internal tree.UpdateITree (source: ITree) (target: ITree) =
if not (source.NodeType = target.NodeType) then failwith "Nodes are incompatible types"
if not (target.Path = source.Path) then target.Path <- source.Path
if source.NodeType = TreeFile then tree.UpdateRenditions source target
member internal tree.SyncStep() =
match tree.State with
| TreeNew a ->
let target = tree.CreateBFromA
tree.UpdateITree a target
//tree.BTree.AddChild target
| TreeBoth(a,b) ->
let target = b
tree.UpdateITree a target
//tree.BTree.ModifyChild target
| TreeDeleted b ->
()
//tree.BTree.DeleteChild b
member t.Sync() =
t.Compare()
//t.BTree.Commit()
// Now I want to synchronize between a tree of type T and a tree of type U
let pt = new TProvider()
let ut = new UProvider()
let c = TreeComparison.Create(pt, "/start", ut , "/path")
c.Sync()
问题可能与 CreateSubtree 相关。如果您注释掉其中一个:
docTree.Compare()
行tree.UpdateITree
调用并将它们替换为 ()
,然后推理保持通用,一切都很可爱。
这真是一个难题。我尝试将第二个 block 中的“比较”函数移出类型并将它们定义为递归函数;我已经尝试过一百万种注释或强制打字的方法。我就是不明白!
我考虑的最后一个解决方案是为子同步的比较类型和函数制作一个完全独立(且重复)的实现。但这是丑陋且可怕的。
感谢您阅读到这里!谢啦!
最佳答案
我还没有对代码进行足够的分析来找出原因,但添加了
member internal tree.SyncStep() : unit =
// ^^^^^^
似乎已经解决了。
编辑
另请参阅
Understanding F# Value Restriction Errors
Unknown need for type annotation or cast
需要经验才能非常深入地了解 F# 类型推理算法的功能和局限性。但这个例子似乎属于人们在做非常高级的事情时遇到的一类问题。对于类的成员,F# 推理算法会执行类似的操作
这可能不完全正确;我不太了解它,无法描述该算法,我只是有一个感觉。您可以随时去阅读语言规范。
经常发生的情况是,您到达第 3 点并迫使推理器开始尝试同时求解/约束所有方法体,而实际上这是不必要的,因为,例如也许某些函数有一个简单的具体固定类型。就像 SyncStep 是 unit->unit,但 F# 在步骤 3 中还不知道它,因为签名不明确,它只是说 ok SyncStep 对于某些尚未解决的类型 'a 具有类型“unit -> 'a”并且那么现在 SyncStep 通过引入不必要的变量使所有求解变得不必要地复杂化。
我发现这一点的方式是,第一个警告(此构造导致代码比类型注释指示的更不通用。类型变量“a 已被限制为类型“V”)位于最后一行调用 docTree.Compare() 时的 UpdateRenditions 主体。现在我知道 Compare() 应该是单位 -> 单位。那么我怎么可能会收到关于通用性的警告呢?啊,好吧,编译器当时并不知道返回类型是单位,所以它必须认为某些东西是通用的,但不是。事实上,我可以将返回类型注释添加到 Compare 而不是 SyncStep - 两者都可以。
不管怎样,我实在是太啰嗦了。总结一下
希望有帮助!
关于generics - 强制泛型和接口(interface)上的 F# 类型推断保持宽松,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1809405/
大语言模型具备从文字中推断情感和主题的能力。这种能力可用于获知客户对产品评价的情感、新闻或媒体文章的主题或倾向等。大语言模型的这种推断能力可被应用于舆情分析等场景。 推断可以看作是模型接收文本作为输入
当使用模板模板参数时,我如何推断或删除模板模板的模板类型? 考虑以下 SSCCE: #include #include #include using namespace std; templat
假设我有一些特质: trait A[T] { def foo: T } 一个扩展它的类: class B[T](t: T) extends A[T] { def foo = t } 以及父特征的子特征
一边玩-rectypes在某些时候选择 OCaml 我只是迷路了。 这个表达式几乎可以打字: # fun x -> x x;; - : ('a -> 'b as 'a) -> 'b = 但是这里 O
我正在编写一个类似 CRUD 的应用程序,并且通过主键进行大量查找(主键可以有不同的类型)。所以我定义了以下类型类: {-# LANGUAGE MultiParamTypeClasses #-} cl
我已经创建了关系 A 'is functional parent of' B并定义 'has functional parent'作为 'is functional parent of' 的倒数. '
给定一个使用 Kotlin 版本 1.3.61 和 JOOQ 版本 3.13.1 的系统,这样的方法会构建 union正常查询: val selectCommonPart = coalesce
考虑以下错误代码: fun x = if (null x) then 0 else (take 50 x) : (fun (drop 50 x)) 我注意到,我可以毫无问题地将它加载到
给定一个具有以下类型的函数 a: a::x -> Bool 和以下类型的另一个函数 b: b::Bool -> y 我正在尝试找出推断以下函数类型的步骤: c =\d -> d a b 有人可以帮助解
我正在尝试使用 Infer 工具来分析我的应用代码。我关注了these steps每次我尝试运行 infer -- gradle build 时,我都会收到以下错误: infer -- gradle
所以我制作了这个模板来定义内联仿函数: template struct AsFunctor { template std::invoke_result_t operator()(A
是否可以推断 CRTP 基类中模板化成员函数的返回类型? 虽然推断参数类型效果很好,但它因返回类型而失败。考虑以下示例。 #include template struct base { tem
使用 Series.interpolate 很容易在 Pandas.DataFrame 中插入值,如何进行外推? 例如,给定一个如图所示的 DataFrame,我们如何将它外推 14 个月到 2014
我想知道为什么这不起作用(缺少参数类型)? Seq(1,2,3).toSet.map(_ + 1) 但这确实: val foo = Seq(1,2,3).toSet foo.map(_ + 1)
我没有必要使用 SQLite3 shell 工具来维护一个小型数据库。我正在使用 -header -ascii标志,尽管据我所知,这适用于任何输出选择。我正在寻找一种方法来避免对返回的任何一个值的类型
我有以下组件 type PropTypes = { items: T[], header: (item: T) => React.Element, body: (item: T) => R
我想在 Eclipse/JSDT 中指定实例变量的类型,如下例所示: /** * @constructor */ function A() { /** @type Node */
我正在用 Python 编写一个方法,它看起来像这样: def rgb_to_grayscale(image): print(image.shape) pass 此处预期的类型是 nu
我有一个 my_values 数组,我正在尝试为其推断 true_values 数组中最接近、较小的值。使用下面的 find_nearest 函数并不能完成我想要的。我如何追加它以找到最近的、较小的值
在下面的代码中: template int b(int q, const std::array& types) { int r = q; for (int t : types)
我是一名优秀的程序员,十分优秀!