F# 递归记录定义中的匿名泛型参数
我正在建立一个HFSM并使用一个记录来跟踪状态:F# 递归记录定义中的匿名泛型参数,f#,F#,我正在建立一个HFSM并使用一个记录来跟踪状态: type State<'a> = { CurrentNodeId : int Data : 'a } 类型State这在F#中并不是一个好的解决方案。实际上,我可能只是将状态保持为obj,并将其取消绑定到我期望的类型,或者使用一个简单的区分并集来存储状态,以捕获可能的情况 有一种非常复杂的方法可以做到这一点,这可能是编码你想要的,但会使你的代码看起来非常糟糕(我玩过这个,我不认为我可以
type State<'a> =
{
CurrentNodeId : int
Data : 'a
}
类型State这在F#中并不是一个好的解决方案。实际上,我可能只是将状态保持为obj
,并将其取消绑定到我期望的类型,或者使用一个简单的区分并集来存储状态,以捕获可能的情况
有一种非常复杂的方法可以做到这一点,这可能是编码你想要的,但会使你的代码看起来非常糟糕(我玩过这个,我不认为我可以把它做好)。其思想是使用单个Invoke
方法存储一个接口,该方法是泛型的,并将使用存储在子类中的类型的类型参数进行调用:
type InvokeWithState<'R> =
abstract Invoke<'T> : State<'T, 'R> list -> 'R
and State<'T, 'R> =
{ CurrentNodeId : int
Data : 'T
SubStates : InvokeWithState<'R> -> 'R }
其思想是SubStates
函数将调用Invoke
操作,并为其指定State:State->'R值
让我们记录深度=
{新的HfsmOp,带有
成员x.Invoke(状态)=
让childDepth=
{使用新的InvokeWithState
成员x.调用列表):int=
如果List.isEmpty状态为0
else states |>List.map(fun s->depth.InvokeTomas认为在F#中没有超干净的方法来实现这一点是正确的,但我认为可以比他的方法做得更好一些。基本思想是您需要这样的类型:
let hfsm =
{ CurrentNodeId = 1
Data = "root"
SubStates = fun op ->
op.Invoke
[ { CurrentNodeId = 2
Data = 42
SubStates = fun op -> op.Invoke [] } ] }
type State<'a> = {
CurrentNodeId : int
Data : 'a
SubState : ∃'x. State<'x> list
}
我们可以演示如何使用这些类型和Tomas的递归depth
函数的类似定义
let rec depth = 1 + s.SubStates |> List.map depth |> List.fold max 0
但是我们需要添加一些额外的语法来创建和应用我们的泛型类型(尽管如果您眯着眼睛看,希望核心逻辑仍然很明显):
这不是F#问题-这是您的定义问题-您必须在那里指定类型!因此,如果它有类型“a”或某些常见子类型,请使用它-如果没有,您必须完全删除数据部分和泛型,使用类似于状态的接口,而不使用数据部分,或者移动到obj
:(我只是想说明一下:通常情况下,您并不需要数据部分,因为您可以从NodeId到数据进行映射,如果您考虑一下,所有这些数据都应该是同一类型的。我关心的是所有子状态元素都具有同一类型。但我不知道编译时的类型。我想这是不可能定义的在F#type系统中。能否将SubState
设置为'b
类型?是的,如果所有子状态都具有相同的类型,则可以转到type State={Data:'base;…SubState:State}
-如果你不知道它比ofc的'sub~obj
好一点。我认为可能会做得更好-见我的答案。另外,普通let
-绑定函数可以使用多态递归(递归调用的类型不同于外部调用)-您只需要显式添加泛型类型参数并对定义进行完整注释。很好!是的,我只是懒于使用返回类型。尽管我不确定在定义递归depth
函数时我做错了什么-无论我尝试了什么,它总是抱怨(所以我认为它从不允许您使用不同类型的参数递归调用函数……这就是您需要let rec的原因吗depth@TomasPetricek-是的,类型参数需要在定义中显式,参数和返回类型需要完全注释(如果你没有做到这一切,错误消息就没有多大帮助了…)。我从来没有抽出时间来实施你的解决方案,但后来又重新审视了它,我会接受你的答案。
type State<'a> = {
CurrentNodeId : int
Data : 'a
SubState : ∃'x. State<'x> list
}
∃'x.T<'x> ≡ ∀'z.(∀'x.T<'x> -> 'z) -> 'z
type State<'a> = {
CurrentNodeId : int
Data : 'a
SubStates : SomeStateList
}
and SomeStateList =
abstract Apply : StateListApplication<'z> -> 'z
and StateListApplication<'z> =
abstract Apply : State<'x> list -> 'z
let pack states = { new SomeStateList with member __.Apply a = a.Apply states }
let rec depth = 1 + s.SubStates |> List.map depth |> List.fold max 0
// Full type annotation necessary here to get inner use to typecheck
let rec depth<'a> (s:State<'a>) : int =
1 + s.SubStates.Apply {
new StateListApplication<_> with
member __.Apply l = l |> List.map depth |> List.fold max 0
}
depth {
CurrentNodeId = 1
Data = "test"
SubStates = pack [{ CurrentNodeId = 2
Data = 1uy
SubStates = pack []}]
}