首先;感谢您抽出宝贵时间阅读我的问题。如果您需要或希望我更改某些信息,请告诉我们。
当我传入数组处理函数时,类型推断不起作用,但是当我将函数添加到模块而不是注入它时,它确实有效。
尝试添加类型注释,但这只是被忽略了,F#警告第一次调用时代码不那么通用,然后第二次错误输出错误。
但如果我改变:
let handleAction
//following does not work, comment out next line
(mapItems : 'a -> 'b [] -> int -> ('b -> 'a -> 'b) -> 'b [])
到
let handleAction
//following does not work, comment out next line
(notPassed : 'a -> 'b [] -> int -> ('b -> 'a -> 'b) -> 'b [])
然后它工作得很好。试图删除向上的依赖项,但无法让F#理解类型。
let mapItems
action
state
index
handlerfn =
state
|> Array.indexed
|> Array.map (
fun (i, item) ->
if index < 0 then
handlerfn item action
else if i = index then
handlerfn item action
else
item)
//Mediator calling the handler for the action
let handleAction
//following does not work, comment out next line
(mapItems : 'a -> 'b [] -> int -> ('b -> 'a -> 'b) -> 'b [])
//notPassedIn //uncomment this and it works
//even though mapItems here and mapItems
//passed in are the exact same code
state
action =
match action with
|Pausable action -> //actions specific to pausable stopwatch
let handler =
mapItems
action //warning: less generic
state
action.index
match action.``type`` with
//... pausable actions (added to support pause/continue)
| StopWatch action -> //actions from stop watch
let handler =
mapItems
action//error: wrong type
state
action.index
match action.``type`` with
//...handling stopwatch actions
完整代码在这里: https://github.com/amsterdamharu/programmingbook/tree/example8
(*
stopwatch module
*)
//types
type SWActionType =
| Start of int
type StopWatchAction = {
``type``:SWActionType
//there may be more than one stopwatch in the application
index:int
}
type StartDate =
| NoStartDate
| Date of int
type SingleStopWatchState = {
status:string
}
type StopWatchState = SingleStopWatchState []
//handlers for the stopwatch actions
let handleStart current state =
{state with status = "started"}
//mediator for stopwatch
let StopWatchHandleAction
mapItems
(state:StopWatchState)
(action:StopWatchAction) =
let handler =
mapItems
action
state
action.index
match action.``type`` with
| Start current ->
handler//call handler with state
(fun
(state:SingleStopWatchState)
(action:StopWatchAction) ->
(handleStart current state))
(*
Pausable stopwatch that extends stopwatch and supports
pause action
*)
type PActionType =
| Pause of int
type PausableStopWatchAction = {
``type``:PActionType
index:int
}
type PAction =
| StopWatch of StopWatchAction
| Pausable of PausableStopWatchAction
type SinglePausableStopWatchState = {
status:string
isPaused:bool
}
type PausableStopWatchState = SinglePausableStopWatchState []
//handlers for pausable stopwatch
let handlePause current (state:SinglePausableStopWatchState) =
{state with
status = "paused"
isPaused = true
}
//mediator for pausable stopwatch
let PausableHandleAction
(mapItems : 'a -> 'b [] -> int -> ('b -> 'a -> 'b) -> 'b [])
state
action =
match action with
|Pausable action -> //actions specific to pausable stopwatch
let handler =
mapItems
//warning:This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'PausableStopWatchAction'.
action
state
action.index
match action.``type`` with
| Pause current ->
handler//call handler with state
(fun
state
action ->
(handlePause current state))
| StopWatch action -> //actions from stop watch
let handler =
mapItems
(*
ERROR
This expression was expected to have type
'PausableStopWatchAction'
but here has type
'StopWatchAction'
*)
action
state
action.index
match action.``type`` with
| Start current ->
handler//call handler with state
(fun
state
action -> //would use some of stopwatch handlers here
{state with
status ="started"
})
(*
Application consuming stopwatch and pausable
*)
type ApplicationState = {
stopwatch:StopWatchState
pausablestopwatch:PausableStopWatchState
}
type Action =
| StopWatch of StopWatchAction
| PausableStopWatch of PAction
let ArrayHandler
action
state
index
handlerfn =
state
|> Array.indexed
|> Array.map (
fun (i, item) ->
if index < 0 then
handlerfn item action
else if i = index then
handlerfn item action
else
item)
//application mediator:
let handleAction
(state : ApplicationState)
action =
match action with
| StopWatch
action ->
{state with//return application state
//set the stopwatch state with updated state
// provided by the mediator in stop watch
stopwatch =
StopWatchHandleAction
ArrayHandler state.stopwatch action}
| PausableStopWatch
action ->
{state with//return application state
pausablestopwatch =
PausableHandleAction
ArrayHandler state.pausablestopwatch action}
答案 0 :(得分:6)
函数泛型是函数声明的一部分。将函数作为值传递时,其通用性将丢失。
考虑以下最小的重复:
let mkList x = [x]
let mkTwo (f: 'a -> 'a list) = (f 42), (f "abc")
let two = mkTwo mkList
此程序将导致相同的警告和您获得的相同错误。这是因为,当我说f: 'a -> 'a list
时,类型变量'a
是mkTwo
的属性,而不是f
的属性。我们可以通过明确声明来更清楚地说明这一点:
let mkTwo<'a> (f: 'a -> 'a list) = (f 42), (f "abc")
这意味着,在每次执行mkTwo
时,必须只有一个 'a
。在'a
执行期间,mkTwo
无法更改。
这暗示了类型推断:第一次编译器遇到表达式f 42
时,它认为&#34; 嘿,f
被调用{{1} }参数在这里,所以int
必须是'a
&#34; - 并向你发出一个有用的警告说&#34; 看起来,你说这应该是通用的,但你实际上使用的是具体类型int
。此构造使此函数不如声明&#34;。
然后,编译器遇到表达式int
。由于编译器已经决定f "abc"
,因此'a = int
,它会抱怨f : int -> int list
是错误的参数类型。
在原始代码中,函数为string
,并且您使用两种不同类型的参数调用它:第一次使用mapItems
(并获得警告),以及第二次使用PausableStopWatchAction
(并收到错误)。
这个问题有两种通用解决方案:
StopWatchAction
在这里,我两次都传递完全相同的函数let mkList x = [x]
let mkTwo f g = (f 42), (g "abc")
let two = mkTwo mkList mkList
。在每种情况下,函数都会失去通用性,但它会以两种不同的方式失去:第一次变为mkList
,第二次变为int -> int list
。这样,string -> string list
将它视为两个不同类型的不同函数,因此可以将它应用于不同的参数。
与函数不同,接口方法在接口作为参数传递时不会失去通用性。因此,您可以将mkTwo
函数包装在一个界面中并使用它:
mapItems
这无疑比纯功能代码更笨重,但它完成了工作。
但是在你的具体情况下,甚至都不需要,因为你可以&#34;烘烤&#34;将type MkList =
abstract member mkList : 'a -> 'a list
let mkList = { new MkList with member this.mkList x = [x] }
let mkTwo (f: MkList) = (f.mkList 42), (f.mkList "abc")
let two = mkTwo mkList
转到action
(这里我假设你实际上在handlerfn
内使用了action
,即使你发布的代码没有表明这一点:
handlerfn