F# 类型约束 - 类型变量 'a has been constrained to be type ''b'

F# Type Constraint - The type variable 'a has been constrained to be type ''b'

我正在尝试使一个函数正常工作,该函数可以打开信封类型,将函数应用于内容和 returns 信封类型。有点像墨西哥卷饼的绑定功能。

type Envelope<'a> =
  { Content : 'a
  ; Errors : string list
  }

let (!>) f e =
    let {Content=content:'a; Errors=errors} = e

    match errors with
    | [] -> e : Envelope<'a>
    | _ -> f content : Envelope<'b>

错误是:

This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type ''b'.

我有一个 "feeling" 为什么它是错误的,有时我返回一个 Envelope<'a> 而其他时候我返回一个 Envelope<'b>

我怎样才能让它工作?我试图使它 "work" 就像我将绑定函数一样,例如,选项类型:

let (>>=) f o =
  match o with
  | Some v -> f v
  | None -> None 

问题是匹配的两种情况应该return相同的类型,否则在类型系统中没有意义。

您需要构建一个新的信封,但我想问题是如果出现错误您不想计算 f,所以一个 hacky 的方法是:

type Envelope<'a> =
  { Content : 'a
  ; Errors : string list
  }

let (!>) f e =
    let {Content=content:'a; Errors=errors} = e

    match errors with
    | [] -> {Content = Unchecked.defaultof<_>; Errors = e.Errors }  : Envelope<'b>
    | _ -> f content : Envelope<'b>

但这不是您想要的,因为您会丢失内容。

正确的方法是使用区分联合,而不是记录,但我想你想要的是在出现错误的时候一直应用补偿函数,所以在那种情况下,你的补偿函数不能' t 是多态的,因此是原始错误消息。

在我看来,您要么试图映射错误,要么在出现错误时获取默认值。这里有几个选项

type Envelope<'a> = { 
    Content : 'a
    Errors : string list
}

/// (unit -> 'a) -> Envelope<'a>  -> 'a
let defaultWith f e =
    match e.Errors with
    | [] -> e.Content 
    | _  -> f()

这个获取值,如果有错误则调用函数获取默认值。相当于Option.defaultWith。它不 return Envelope

下一个让您映射错误,仅当存在错误时:

/// (string list -> string list) -> Envelope<'a> -> Envelope<'a>
let mapErrors f e =  
    match e.Errors with
    | [] ->   e
    | _  -> { e  with Errors = f e.Errors }

另一方面,如果有错误,这两个可以让您映射整个信封。您可以创建同一主题的许多变体,我只留下 2 个作为说明:

/// ('a -> Envelope<'a>) -> Envelope<'a> -> Envelope<'a>
let mapIfErrors f e =
    match e.Errors with
    | [] ->   e
    | _  -> f e.Content 

/// ('a -> string list -> Envelope<'a>) -> Envelope<'a> -> Envelope<'a>
let mapIfErrors2 f e =
    match e.Errors with
    | [] ->   e
    | _  -> f e.Content e.Errors