F# 递归记录定义中的匿名泛型参数

F# 递归记录定义中的匿名泛型参数,f#,F#,我正在建立一个HFSM并使用一个记录来跟踪状态: type State<'a> = { CurrentNodeId : int Data : 'a } 类型State这在F#中并不是一个好的解决方案。实际上,我可能只是将状态保持为obj,并将其取消绑定到我期望的类型,或者使用一个简单的区分并集来存储状态,以捕获可能的情况 有一种非常复杂的方法可以做到这一点,这可能是编码你想要的,但会使你的代码看起来非常糟糕(我玩过这个,我不认为我可以

我正在建立一个HFSM并使用一个记录来跟踪状态:

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 []}]
}