开发者

F# casting and generics

Trying to learn some F# and I've run into a couple of hangups.

Here's the code:

#light
module HtmlModule

type HtmlString(text:string) =
    override x.ToString() = text

type HtmlAttribute(key:string, value:string) =
    inherit HtmlString(key + "=\"" + value + "\"");

type HtmlElement(tag: string, ?contents:list<'a> when 'a :> HtmlString) =
    inherit HtmlString("")
    let innerContents = defaultArg contents []
    let castToHtmlAttribute (hs:HtmlString) : Option<HtmlAttribute> =
        match hs with
        | :? HtmlAttribute as temp -> Some(temp)
        | _ -> None
    member x.tag = tag
    member x.contents = innerContents |> List.filter(fun x -> (castToHtmlAttribute x).IsNone)
    member x.attributes = innerContents |> List.map(fun x -> (castToHtmlAttribute x)) |> List.filter(fun x-> x.IsSome) |> List.map(fun x->x.Value)

    override x.ToString() =
        let sb = System.Text.StringBuilder()
        sb.Append("<" + x.tag)

        for att in x.attributes do
            sb.Append(" " + att.ToString())

        sb.Append(">")

        for item in x.contents do
            sb.Append(item.ToString())

        sb.Append("</" + x.tag + ">")
        sb.ToString()



let element tag contents = new HtmlElement(tag, contents)
let div contents = element "div" contents
let p contents = element "p" contents
let text text = new HtmlString(text)
let attr key value = new HtmlAttribute(key, value)
let classes value = attr "class" value

and a quick console program to show the problem:

#light

open HtmlModule


let stuff = [for a in 1 .. 10 do yield p [text ("some String " + a开发者_如何学Go.ToString())]]

let elem = div [
            attr "class" "someClass";
            text "This is some inner text";
            div stuff;
        ]

let result = elem.ToString()
printfn "%A"  result

the compiler will not allow the the line

div stuff;

It states:

"Error 7 Type mismatch. Expecting a HtmlString list but given a HtmlElement list The type 'HtmlString' does not match the type 'HtmlElement'"

This problem can be tracked to the castToHtmlAttribute method signature

 let castToHtmlAttribute (hs:HtmlString) : Option<HtmlAttribute> =

If I make this line more generic like so:

let castToHtmlAttribute (hs:'a when 'a :> HtmlString) : Option<HtmlAttribute> =

I get a new error:

"Error 2 This runtime coercion or type test from type 'a to HtmlAttribute involves an indeterminate type based on information prior to this program point. Runtime type tests are not allowed on some types. Further type annotations are needed."

Any help is appreciated.


The diagnostic here is not great.

You can fix the code like so:

let stuff = [for a in 1 .. 10 do 
               yield (p [text ("some String " + a.ToString())]) :> HtmlString]

where I've inserted an explicit upcast to the base type.


Expecting a HtmlString list but given a HtmlElement list The type 'HtmlString' does not match the type 'HtmlElement'"

This states the problem quite well. For F#, a List<HTMLElement> is not a List<HTMLString> like a List<Orange> is no List<Fruit>.

If it were, you could write

Apple :: oranges

Since this doesn't make any sense, F# expects you to cast downcast explicitly. Just tell div to work with a generic list.

In short, you can change the type HTMLString list to a polymorphic #HTMLString list or cast the arguments or stuff by hand.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜