Type error when implementing finger trees

后端 未结 1 1622
谎友^
谎友^ 2021-01-18 08:13

I wanted to play around with 2-3 finger trees as described in the (Haskell) paper by Hinze (see also this blog).

type Node<\'a> =
    | Node2 of \'a *          


        
相关标签:
1条回答
  • 2021-01-18 08:28

    Functions like viewl or prepend rely on polymorphic recursion: the recursive call to prepend takes an argument of a different type than the original call. You can define such functions in F#, but as you discovered they require full type annotations (or else you get a very confusing error message). In particular, note that the type parameters must be explicit in the function's definition (though they can typically be inferred at call sites). So the first problem is that you need to specify viewl<'a> in the definition.

    However, there's an extremely subtle second problem, which concerns Digit<_>.OfList. Try sending the first chunk of code to F# interactive and looking at the signatures of the resulting definitions: you'll see static member OfList : (obj list -> Digit<obj>), which will subsequently make it so that viewl can't be defined correctly. So what happened? You haven't given a signature to OfList, so it won't be a generic method (functions will be generalized, but members never will be). But the compiler also can't infer that you intended for the input list to be of type 'a list where the 'a is the type's generic parameter - why would it infer this specific type rather than int list or string list, etc.? So it chooses a boring monomorphic default (obj list), unless you do something in subsequent code to constrain it to a different concrete monomorphic type. Instead, you need to add a signature to Digit and then everything will be fine.

    Often in F# it is idiomatic to create a separate module per-type to define related functions like ToList, etc. Since function definitions are generalized, this would also have avoided the Digit problem you face here. That is, you could structure your code like this:

    type Node<'a> =
        | Node2 of 'a * 'a
        | Node3 of 'a * 'a * 'a
    
    module Node =
        let ofList = function
        | [a; b] -> Node2(a, b)
        | [a; b; c] -> Node3(a, b, c)
        | _ -> failwith "Only lists of length 2 or 3 accepted!"
    
        let toList = function
        | Node2(a, b) -> [a; b]
        | Node3(a, b, c) -> [a; b; c]
    
    type Digit<'a> =
        | One of 'a
        | Two of 'a * 'a
        | Three of 'a * 'a * 'a
        | Four of 'a * 'a * 'a * 'a
    
    [<NoComparison>]
    [<NoEquality>]
    type FingerTree<'a> =
        | Empty
        | Single of 'a
        | Deep of Digit<'a> * FingerTree<Node<'a>> * Digit<'a>
    
    module Digit =
        let ofList = function
        | [a] -> One(a)
        | [a; b] -> Two(a, b)
        | [a; b; c] -> Three(a, b, c)
        | [a; b; c; d] -> Four(a, b, c, d)
        | _ -> failwith "Only lists of length 1 to 4 accepted!"
    
        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]
    
        let append x = function
        | One a -> Two(a, x)
        | Two(a, b) -> Three(a, b, x)
        | Three(a, b, c) -> Four(a, b, c, x)
        | _ -> failwith "Cannot prepend to Digit.Four!"
    
        let prepend x = function
        | One a -> Two(x, a)
        | Two(a, b) -> Three(x, a, b)
        | Three(a, b, c) -> Four(x, a, b, c)
        | _ -> failwith "Cannot prepend to Digit.Four!"
    
        let promote = function
        | One a -> Single a
        | Two(a, b) -> Deep(One a, Empty, One b)
        | Three(a, b, c) -> Deep(One a, Empty, Two(b, c))
        | Four(a, b, c, d) -> Deep(Two(a, b), Empty, Two(c, d))
    
    type View<'a> = Nil | View of 'a * FingerTree<'a>
    
    let rec viewl<'a> : FingerTree<'a> -> View<'a> = function
        | Empty -> Nil
        | Single x -> View(x, Empty)
        | Deep(One x, deeper, suffix) ->
            let rest =
                match viewl deeper with
                | Nil -> suffix |> Digit.promote
                | View (node, rest) ->
                    let prefix = node |> Node.toList |> Digit.ofList
                    Deep(prefix, rest, suffix)
            View(x, rest)
        | Deep(prefix, deeper, suffix) ->
            match prefix |> Digit.toList with
            | x::xs ->
                View(x, Deep(Digit.ofList xs, deeper, suffix))
            | _ -> failwith "Impossible!"
    
    0 讨论(0)
提交回复
热议问题