F # Сглаживание вложенных кортежей в вычислительном выражении

У меня есть вычислительное выражение, которое я хочу вернуть в виде уплощенного кортежа в качестве первого элемента и int в качестве второго. Я пытаюсь использовать для этого перегрузку метода. Прямо сейчас компилятор выдает ошибку, говоря, что он не может найти уникальную перегрузку. Не знаю, как помочь компилятору разобраться. Мне это кажется детерминированным.

type IntBuilder () =

    member inline this.Yield (i:int) =
        i

    member inline this.For(source:seq<'a>, body:'a -> seq<'b * int>) =
        source
        |> Seq.collect (fun x -> body x |> Seq.map (fun (idx, i) -> (x, idx), i))

    member inline this.For(source:seq<'a>, body:'a -> int) =
        source |> Seq.map (fun x -> x, body x)

    member inline this.Run(source:seq<('a * ('b * ('c * 'd))) * 'v>) =
        source 
        |> Seq.map (fun ((x, (y, (z, a))), d) -> (x, y, z, a), d)

    member inline this.Run(source:seq<('a * ('b * 'c)) * 'v>) =
        source 
        |> Seq.map (fun ((x, (y, z)), d) -> (x, y, z), d)

    member inline this.Run(source:seq<('a * 'b) * 'v>) =
        source 
        |> Seq.map (fun ((x, y), d) -> (x, y), d)

    member inline this.Run(source:seq<'a * 'v>) =
        source 
        |> Seq.map (fun (x, d) -> x, d)

let intBuilder = IntBuilder ()
let c = 
    intBuilder {
        for i in 1..2 do
            for j in 1..2 do
                for k in 1..2 do
                    for l in 1..2 -> 
                         i + j + k + l
    }

// What I get
c : seq<(int * (int * (int * int))) * int>

// What I want
c : seq<(int * int * int * int) * int>

В этом случае c относится к типу seq<(int * (int * (int * int))) * int>. Я хочу, чтобы вычисление IntBuilder возвращало seq<(int * int * int * int), int>. Как мне этого добиться?


person Matthew Crews    schedule 09.06.2020    source источник


Ответы (3)


Похоже, единственный способ заставить это работать - обернуть их все конкретными типами:

type T1<'a> = | T1 of seq<'a * int>
type T2<'a,'b> = | T2 of seq<('a * 'b) * int>
type T3<'a,'b,'c> = | T3 of seq<('a * ('b * 'c)) * int>
type T4<'a,'b,'c,'d> = | T4 of seq<('a * ('b * ('c * 'd))) * int>
type T5<'a,'b,'c,'d,'e> = | T5 of seq<('a * ('b * ('c * ('d * 'e)))) * int>

type IntBuilder () =
    member this.Yield (i:int) =
        i

    member this.For(source:seq<'a>, body:'a -> int) =
        source |> Seq.map (fun x -> x, body x)
        |> T1.T1

     member this.For(source:seq<'a>, body:'a -> T1<'b>) =
        source
        |> Seq.collect (fun x -> 
            body x 
            |> fun (T1.T1 x) -> x
            |> Seq.map (fun (idx, i) -> (x, idx), i))
        |> T2.T2

    member this.For(source:seq<'a>, body:'a -> T2<'b,'c>) =
       source
       |> Seq.collect (fun x -> 
           body x 
           |> fun (T2.T2 x) -> x
           |> Seq.map (fun (idx, i) -> (x, idx), i))
       |> T3.T3

    member this.For(source:seq<'a>, body:'a -> T3<'b,'c,'d>) =
       source
       |> Seq.collect (fun x -> 
           body x 
           |> fun (T3.T3 x) -> x
           |> Seq.map (fun (idx, i) -> (x, idx), i))
       |> T4.T4

    member this.For(source:seq<'a>, body:'a -> T4<'b,'c,'d,'e>) =
        source
        |> Seq.collect (fun x -> 
            body x 
            |> fun (T4.T4 x) -> x
            |> Seq.map (fun (idx, i) -> (x, idx), i))
        |> T5.T5

    member inline this.Run(T1.T1 source) =
        source 
        |> Seq.map (fun (x, d) -> x, d)

    member inline this.Run(T2.T2 source) =
        source 
        |> Seq.map (fun ((x, y), d) -> (x, y), d)

    member inline this.Run(T3.T3 source) =
        source 
        |> Seq.map (fun ((x, (y, z)), d) -> (x, y, z), d)

    member inline this.Run(T4.T4 source) =
        source 
        |> Seq.map (fun ((x, (y, (z, a))), d) -> (x, y, z, a), d)

let intBuilder = IntBuilder ()


let c = 
    intBuilder {
        for i in 1..2 do
            for j in 1..2 do
                for k in 1..2 do
                    for l in 1..2 -> 
                         i + j + k + l
    }

Выход FSI:

val c : seq<(int * int * int * int) * int>

> c;;
val it : seq<(int * int * int * int) * int> =
  seq
    [((1, 1, 1, 1), 4); ((1, 1, 1, 2), 5); ((1, 1, 2, 1), 5);
     ((1, 1, 2, 2), 6); ...]
person Cody Johnson    schedule 10.06.2020

Эту проблему можно решить с помощью встраивания и разрешения перегрузки, но в общем смысле (afaik) невозможно иметь функцию, которая может возвращать тип int * int * ... на основе произвольного типа введенных данных. Возможно, кто-то другой сможет это взвесить.

Однако мы можем добиться выравнивания с помощью типового тестирования во время выполнения.

let flatten tuple =
    let rec fold (tuple: obj) acc = 
        match tuple with
        | :? int as v -> v::acc
        | :? ITuple as rest ->  
            let next, value = rest.[0], rest.[1] :?> int
            fold next (value::acc)
        | _ -> failwith "Unexpected type"

    fold tuple []

> flatten ((1, 2), 3);;
val it : int list = [1; 2; 3]
person Asti    schedule 09.06.2020

Ответ на отредактированный вопрос:

Компилятор не может разрешить правильную перегрузку, потому что

seq<('a * ('b* ('c * 'd))) * 'v>) 

является подтипом:

seq<'u * 'v> where 'u = 'a * ('b* ('c * 'd)))

и так далее для других типов. Так что ограничьте некоторые типы конкретным типом. Ограничения самого внутреннего типа кортежа должно быть достаточно.

Например:

member  this.Run(source:seq<('a * ('b* ('c* int))) * 'v>) =
    source 
    |> Seq.map (fun ((x, (y, (z, a))), d) -> (x, y, z, a), d)

member  this.Run(source:seq<('a * ('b* int)) * 'v>) =
    source 
    |> Seq.map (fun ((x, (y, z)), d) -> (x, y, z), d)

member  this.Run(source:seq<('a * int) * 'v>) =
    source 
    |> Seq.map (fun ((x, y), d) -> (x, y), d)

member  this.Run(source:seq<int * 'v>) =
    source 
    |> Seq.map (fun (x, d) -> x, d)
person Asti    schedule 10.06.2020