Ограничение типа F # - переменная типа 'a ограничена типом' 'b' - PullRequest
0 голосов
/ 26 ноября 2018

Я пытаюсь заставить работать функцию, которая разворачивает тип конверта, применяет функцию к содержимому и возвращает тип конверта.Вроде как функция привязки буррито.

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'.

У меня есть «чувство», почему это неправильно, иногда яЯ возвращаю Envelope<'a>, а другой раз я возвращаю Envelope<'b>.

Как мне заставить это работать?Я пытаюсь заставить его «работать», как если бы я использовал функцию привязки, например, для типа Option:

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

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

Мне кажется, вы пытаетесь сопоставить ошибку или получить значение по умолчанию в случае ошибок.Вот несколько опций

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.Он не возвращает 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
0 голосов
/ 26 ноября 2018

Проблема в том, что оба случая соответствия должны возвращать один и тот же тип, иначе это не имеет смысла в системе типов.

Вам необходимо создать новый конверт, но я думаю, что проблема в том, чточто вы не хотите вычислять f, если есть ошибки, поэтому хакерский способ сделать это будет:

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>

Но это не то, что вы хотите, так как вы потеряете содержимое.

Надлежащим способом было бы использовать Дискриминационный Союз вместо записи, но я думаю, что вы хотите применять функцию компенсации все время, когда есть ошибки, поэтому в этом случае ваша функция компенсации не может бытьполиморфный, поэтому оригинальное сообщение об ошибке.

...