传递map函数时,输入推断不起作用 [英] Type inference not working when passing map function

查看:123
本文介绍了传递map函数时,输入推断不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先;感谢您花时间阅读我的问题。如果您还需要更多信息或希望我改变某些内容,请告诉我。



当我传递数组处理函数时,类型推断不起作用,但是,当我添加功能的模块,而不是注入它然后它的工作。

试图添加类型注释,但是这只是被忽略,F#警告代码不通用时



但如果我改变了:

  let handleAction 
//下面不行,注释掉下一行
(mapItems:'a - >'b [] - > int - > ('b - >'a - >'b) - >'b [])

to

  let handleAction 
//下面不行,下一行注释
(notPassed: 'a - >'b [] - > int - >('b - >'a - >'b) - >'b [])
/ pre>

然后它只用f国家统计局。尝试删除向上的依赖项,但无法让F#理解该类型。

 让mapItems 
动作
状态
索引
handlerfn =
状态
|> Array.indexed
|> Array.map(
fun(i,item) - >
if index< 0 then
handlerfn item action
else if i = index then
handlerfn item action
else
item)

//调解处理程序的中介程序
让handleAction
//下面的程序不起作用,注释掉下一行
(mapItems:'a - >'b [] - > int - >('b - >'a - >'b) - >'b [])
/ / notPassedIn //取消注释,并且它的工作原理是
//即使这里的mapItems和mapItems
//传递的是完全相同的代码
state
action =
match行动与
|可疑动作 - > //特定于pausable秒表的动作
let处理程序=
mapItems
动作//警告:不通用
状态
动作索引
匹配动作.` `type`` with
// ...可pausable操作(添加到支持暂停/继续)
| StopWatch操作 - > //停止监视的动作
let handler =
mapItems
动作//错误:错误类型
状态
action.index
匹配action.```
//...handling秒表动作

完整的代码在这里:
https://github.com/amsterdamharu/programmingbook/tree/example8

 (* 
秒表模块
*)
//类型
键入SWActionType =
| Start
类型StopWatchAction = {
``type``:SWActionType
//在应用程序中可能有多个秒表
index:int
}
类型StartDate =
| NoStartDate
| int
类型的日期SingleStopWatchState = {
status:string
}
类型StopWatchState = SingleStopWatchState []
//秒表动作的处理程序
let handleStart当前状态=
{state with status =started}
//秒表的中介
让StopWatchHandleAction
mapItems
(状态:StopWatchState)
action:StopWatchAction)=
let handler =
mapItems
action
state
action.index
与$ b $匹配action.````` b |开始当前 - >
处理程序//调用状态为
(fun
(state:SingleStopWatchState)
(action:StopWatchAction) - >
(handleStart current state))
(*
可延期秒表并支持
暂停动作
*)
的可停用秒表类型PActionType =
|暂停使用int
类型PausableStopWatchAction = {
``type``:PActionType
index:int
}
type PAction =
| StopWatchAction
|秒表PausableStopWatchAction
类型SinglePausableStopWatchState = {
status:string
isPaused:bool
}
类型PausableStopWatchState = SinglePausableStopWatchState []
//用于可停用秒表的处理程序
let handlePause current(state:SinglePausableStopWatchState)=
{state with
status =paused
isPaused = true
}
//可调用的中介器stopwatch
让PausableHandleAction
(mapItems:'a - >'b [] - > int - >('b - >'a - >'b) - >'b [ ])
状态
动作=

匹配动作|可行动 - - > //特定于pausable秒表的操作
let handler =
mapItems
//警告:此构造使代码不如类型注释所指示的通用。类型变量'a被约束为类型'PausableStopWatchAction'。
动作
状态
动作索引

匹配action.````` |暂停当前​​ - >
处理程序//调用具有状态
(fun
state
action - >
(handlePause current state))
的处理程序
| StopWatch操作 - > //停止监视的动作
let handler =
mapItems
(*
ERROR
此表达式预计有类型
'PausableStopWatchAction'
但是这里有类型
'StopWatchAction'
*)
动作
状态
动作索引
与$ b匹配action.````` $ b |开始当前 - >
处理程序//调用状态为
的处理程序(有趣的
状态
动作 - > //在这里使用一些秒表处理程序
{带有
的状态状态=开始
))
(*
应用程序使用秒表和可以使用的
*)
类型ApplicationState = {
stopwatch:StopWatchState
pausablestopwatch:PausableStopWatchState

type Action =
| StopWatchAction
|秒表PausableStopWatch of PAction
让ArrayHandler
动作

索引
handlerfn =

|> Array.indexed
|> Array.map(
fun(i,item) - >
if index< 0 then
handlerfn item action
else if i = index then
handlerfn item动作
else
item)
//应用介体:
让handleAction
(state:ApplicationState)
动作=
匹配
| StopWatch
操作 - >
{//状态返回应用程序状态
//设置秒表状态为更新状态
//中介提供的停止监视
stopwatch =
StopWatchHandleAction
ArrayHandler state.stopwatch action}
| PausableStopWatch
操作 - >
{//返回应用程序状态的状态
pausablestopwatch =
PausableHandleAction
ArrayHandler state.pausablestopwatch action}


解决方案

函数泛型是函数声明的一部分。当你将一个函数作为一个值传递时,它的通用性会丢失。



请考虑以下最小代数:

<$ p (f:'a - >'列表)=(f 42),(fabc)
让mkList x = [x]
let mkTwo let two = mkTwo mkList

这个程序会导致相同的警告和相同的错误。这是因为,当我说 f:'a - > 'list ,类型变量'a mkTwo 的一个属性,不属性 f 。我们可以通过明确声明来更清楚地说明:

  let mkTwo< a> (f:'a  - >'列表)=(f 42),(fabc)

这意味着,在 mkTwo 的每个给定执行中,只能有一个 'a 'a mkTwo 执行期间不能更改。



这对于类型推断有一个含义:第一次编译器遇到表达式 f 42 ,它认为 hey, f 在这里用 int 参数调用,所以'a 必须是 int - 并且向您发出有用的警告,指出 look,您说这应该是通用的,但实际上您将它与具体类型<$ c $一起使用c> int 。这个结构使得这个函数的泛型比声明的更低。



然后,编译器遇到表达式 fabc。由于编译器已经确定'a = int ,因此 f:int - > int list ,它抱怨 string 是错误的参数类型。



原始代码,函数是 mapItems ,并且您使用两种不同类型的参数调用它:第一次使用 PausableStopWatchAction (并获得警告),第二次使用 StopWatchAction (并且出现错误)。

这个问题有两种通用的解决方案:

通用解决方案1:传递函数两次

  let mkList x = [x] 
let mkTwo fg =(f 42),(gabc)
let two = mkTwo mkList mkList

在这里,我传递了两次完全相同的函数 mkList 。在每种情况下,函数都失去通用性,但它以两种不同的方式丢失它:第一次变为 int - > int list ,第二次变为 string - >字符串列表。这样, mkTwo 将它视为两种不同类型的函数,因此可以将它应用于不同的参数。



< h2>通用解决方案2:使用接口



接口方法与函数不同,在接口作为参数传递时不会失去通用性。所以你可以在接口中包装你的 mapItems 函数并使用它:

  type MkList = 
抽象成员mkList:'a - > 'a list

let mkList = {new MkList with member this.mkList x = [x]}
let mkTwo(f:MkList)=(f.mkList 42),(f。 mkListabc)
let two = mkTwo mkList

函数代码,但它完成了工作。



代码的具体解决方案



但是在您的具体情况,因为你可以将动作右移到 handlerfn
(在这里我假设你实际上在 handlerfn 中使用 action ,即使你发布的代码没有显示):

 让mapItems 
state
索引
handlerfn =
state
|> Array.indexed
|> Array.map(
fun(i,item) - >
如果index <0,则
handlerfn item
else if i = index then
handlerfn item
else
item)

...

let handleAction
(mapItems:'a [] - > int - >( 'a - > a) - >'a [])
状态
动作=

匹配动作|可行动 - > //特定于可停秒表的操作
let处理程序=
mapItems
状态
action.index
匹配action.````` with
|暂停当前​​ - >
处理程序//调用状态为
的处理程序(乐趣状态 - >
(handlePause当前状态))
| StopWatch操作 - > //停止监视的动作
让处理程序=
mapItems
state
action.index

匹配action.```` |开始当前 - >
处理程序//调用状态为
的处理程序(乐趣状态 - >
//在这里使用一些秒表处理程序
{具有
status =started
})


First of all; thank you for taking the time to read my question. If there is any more information you need or would like me to change something please let me know.

When I pass in an array handler function the type inference does not work, but when I add the function to the module instead of injecting it then it does work.

Tried adding type annotation but that's just ignored and F# warns about code being less generic when calling it the first time and then errors out with wrong type the second time.

But if I change:

let handleAction
  //following does not work, comment out next line
  (mapItems : 'a -> 'b [] -> int -> ('b -> 'a -> 'b) -> 'b [])

to

let handleAction
  //following does not work, comment out next line
  (notPassed : 'a -> 'b [] -> int -> ('b -> 'a -> 'b) -> 'b [])

Then it works just fine. Trying to remove upwards dependencies but can't get F# to understand the type.

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

Full code is here: 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}

解决方案

Function genericity is part of the function declaration. When you pass a function as a value, its genericity is lost.

Consider the following minimal repro:

let mkList x = [x]
let mkTwo (f: 'a -> 'a list) = (f 42), (f "abc")
let two = mkTwo mkList

This program will cause the same warning and same error you're getting. This is because, when I say f: 'a -> 'a list, the type variable 'a is a property of mkTwo, not property of f. We could make this clearer by declaring it explicitly:

let mkTwo<'a> (f: 'a -> 'a list) = (f 42), (f "abc")

This means that, on every given execution of mkTwo, there has to be only one 'a. The 'a cannot change during an mkTwo execution.

This has an implication for type inference: the first time the compiler comes across the expression f 42, it thinks "hey, f is called with an int argument here, so 'a must be int" - and issues you a helpful warning saying "look, you say this should be generic, but you're actually using it with a concrete type int. This construct makes this function less generic than declared".

Then, the compiler comes across the expression f "abc". Since the compiler has already decided that 'a = int, and therefore f : int -> int list, it complains that string is the wrong parameter type.

In your original code, the function is mapItems, and you're calling it with two different types of arguments: the first time with PausableStopWatchAction (and get a warning), and the second time with StopWatchAction (and get an error).

There are two general solutions to this problem:

General solution 1: pass the function twice

let mkList x = [x]
let mkTwo f g = (f 42), (g "abc")
let two = mkTwo mkList mkList

Here, I pass the exact same function mkList both times. In each case the function loses genericity, but it loses it in two different ways: the first time it becomes int -> int list, and the second time it becomes string -> string list. This way, mkTwo sees it as two different functions, of different types, and so can apply it to different arguments.

General solution 2: use an interface

Interface methods, unlike functions, do not lose genericity when the interface is passed as argument. So you can wrap your mapItems function in an interface and use it:

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

This is admittedly more bulky than pure functional code, but it gets the job done.

Specific solution for your code

But in your specific case, that is all not even required, because you could "bake" the action right into handlerfn (here I assume that you're actually using action inside handlerfn, even though the code you posted doesn't show that):

let mapItems
  state 
  index
  handlerfn =
    state
      |> Array.indexed
      |> Array.map (
        fun (i, item) ->
          if index < 0 then
            handlerfn item 
          else if i = index then
            handlerfn item 
          else
            item)

...

let handleAction
  (mapItems : 'a [] -> int -> ('a -> 'a) -> 'a [])
  state
  action =
    match action with
    |Pausable action -> //actions specific to pausable stopwatch
        let handler = 
          mapItems
            state
            action.index
        match action.``type`` with
         | Pause current ->
             handler//call handler with state
               (fun state ->
                     (handlePause current state))
    | StopWatch action -> //actions from stop watch
       let handler = 
         mapItems
           state
           action.index
       match action.``type`` with
         | Start current ->
             handler//call handler with state
               (fun state ->
                   //would use some of stopwatch handlers here
                   {state with
                     status ="started"
                   })

这篇关于传递map函数时,输入推断不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆