Как выражения последовательности и полиморфная рекурсия играют вместе?

Этот проект действительно является источником вопросов для меня.

Я уже узнал о полиморфной рекурсии и понимаю, почему это особый случай, и поэтому F# требует полных аннотаций типа.

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

Мне кажется, что использование выражения для вычисления как-то связано с этим. Это сжатая рабочая версия:

module ThisWorks =

    module Node =
        type Node<'a> =
            | Node2 of 'a * 'a
            | Node3 of 'a * 'a * 'a

        let toList = function
            | Node2(a, b) -> [a; b]
            | Node3(a, b, c) -> [a; b; c]

    module Digit =
        type Digit<'a> =
            | One of 'a
            | Two of 'a * 'a
            | Three of 'a * 'a * 'a
            | Four of 'a * 'a * 'a * 'a

        let toList = function
            | One a -> [a]
            | Two(a, b) -> [a; b]
            | Three(a, b, c) -> [a; b; c]
            | Four(a, b, c, d) -> [a; b; c; d]

    module FingerTree =
        open Node
        open Digit

        type FingerTree<'a> =
            | Empty
            | Single of 'a
            | Deep of Digit<'a> * Lazy<FingerTree<Node<'a>>> * Digit<'a>

        let rec toSeq<'a> (tree:FingerTree<'a>) : seq<'a> = seq {
            match tree with
            | Single single ->
                yield single
            | Deep(prefix, Lazy deeper, suffix) ->
                yield! prefix |> Digit.toList
                yield! deeper |> toSeq |> Seq.collect Node.toList
                yield! suffix |> Digit.toList
            | Empty -> ()
        }

То, что мне не удается собрать, это:

module ThisDoesnt =

    module Monoids =
        type IMonoid<'m> =
            abstract Zero:'m
            abstract Plus:'m -> 'm

        type IMeasured<'m when 'm :> IMonoid<'m>> =
            abstract Measure:'m

        type Size(value) =
            new() = Size 0

            member __.Value = value

            interface IMonoid<Size> with
                member __.Zero = Size()
                member __.Plus rhs = Size(value + rhs.Value)

        type Value<'a> =
            | Value of 'a

            interface IMeasured<Size> with
                member __.Measure = Size 1

    open Monoids

    module Node =
        type Node<'m, 'a when 'm :> IMonoid<'m>> =
            | Node2 of 'm * 'a * 'a
            | Node3 of 'm * 'a * 'a * 'a

        let toList = function
            | Node2(_, a, b) -> [a; b]
            | Node3(_, a, b, c) -> [a; b; c]

    module Digit =
        type Digit<'m, 'a when 'm :> IMonoid<'m>> =
            | One of 'a
            | Two of 'a * 'a
            | Three of 'a * 'a * 'a
            | Four of 'a * 'a * 'a * 'a

        let toList = function
            | One a -> [a]
            | Two(a, b) -> [a; b]
            | Three(a, b, c) -> [a; b; c]
            | Four(a, b, c, d) -> [a; b; c; d]

    module FingerTree =
        open Node
        open Digit

        type FingerTree<'m, 'a when 'm :> IMonoid<'m>> =
            | Empty
            | Single of 'a
            | Deep of 'm * Digit<'m, 'a> * Lazy<FingerTree<'m, Node<'m, 'a>>> * Digit<'m, 'a>

        let unpack (Value v) = v

        let rec toSeq<'a> (tree:FingerTree<Size, Value<'a>>) : seq<'a> = seq {
            match tree with
            | Single(Value single) ->
                yield single
            | Deep(_, prefix, Lazy deeper, suffix) ->
                yield! prefix |> Digit.toList |> List.map unpack

                #if ITERATE
                for (Value deep) in toSeq deeper do
                                    ^^^^^
                    yield deep

                #else

                yield! deeper |> toSeq |> Seq.collect (Node.toList >> List.map unpack)
                                 ^^^^^
                #endif

                yield! suffix |> Digit.toList |> List.map unpack
            | Empty -> ()
        }

Сообщение об ошибке, которое я получаю, говорит

Ошибка типа несоответствие. Ожидая
FingerTree <размер, узел <размер, значение <'a >>> -> ' b
но учитывая
FingerTree <размер, значение <'c >> -> seq <' c>
Тип 'Узел <Размер, Значение <' a >> 'не соответствует типу' Значение <'b> '

и загогулины подчеркивают рекурсивный зов toSeq,

Я знаю, что "глубокий" тип инкапсулирован в Node и в рабочем коде я просто распаковываю его потом. Но здесь компилятор отключается, прежде чем я получу возможность распаковать. Пытаясь for (Value deep) in toSeq deeper do yield deep имеет ту же проблему.

У меня уже есть выход, а именно использовать toSeq "базы" Tree а также Seq.map unpack после этого. Неверно, попытка выдает очень похожее сообщение об ошибке.

Мне любопытно, что заставляет этот код ломаться и как это можно исправить.

1 ответ

Решение

Сообщение об ошибке компилятора мне кажется ясным: toSeq применимо только к значениям типа FingerTree<Size, Value<'a>> для некоторых 'a, но вы пытаетесь вызвать его на значение типа FingerTree<Size,Node<Size,Value<'a>>> вместо этого, который не совместим. Нет ничего специфического для полиморфной рекурсии или выражений последовательности, эти типы просто не совпадают.

Вместо этого кажется, что было бы намного проще сделать toSeq более общий, принимая ввод типа FingerTree<Size, 'a> (без каких-либо ссылок на Value), что позволит включить рекурсивный вызов, который вы хотите. Тогда вы можете легко получить более конкретную функцию, которую вы на самом деле хотите, составив более общие toSeq с Seq.map unpack,

Другие вопросы по тегам