我们在这里开始发霉。我已经在数据的具体表示上测试了一堆树同步代码,现在我需要将它抽象出来,以便它可以与任何支持正确方法的源和目标一起运行。 [实际上,这将是Documentum,SQL层次结构和文件系统等来源;与目标像Solr和自定义的SQL交叉引用存储。]强制仿制药和接口上的F#类型推断保持宽松
棘手的部分是,当我递归一个类型为T
的树并同步到一个类型为U
的树,在某些文件我需要做在当前节点处将第二类型V
与该类型U
进行“子同步”。 (V
代表分层结构里面的一个文件...)并且F#中的类型推断引擎正在驱动着我,在我尝试将子同步添加到V
后,
我代表这个在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
电话
,并与()
替换它们,那么推论保持通用的,一切都是可爱的。
这是一个相当难题。我试过将第二块中的“比较”函数移出该类型,并将它们定义为递归函数;我试过了一百万种注释或强制输入的方式。我只是不明白!
我正在考虑的最后一个解决方案是为子同步制作一个完全独立的(和重复的)比较类型和函数实现。但那是丑陋而可怕的。
谢谢,如果你读了这么久!啧!
注意:我不太了解静态解析类型参数的用例,比如^ a - 也许碰巧是其中之一? – 2009-11-27 15:58:39